> ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Overview Save one Typecast command, and you can make the same kind of audio again whenever you need it. Use the command yourself, keep it in a note, or hand it to an AI agent so it can generate speech, save files, and create captions with the same settings every time. This is useful when you create content repeatedly and do not want to rebuild the same Typecast request from scratch. Learn one or two commands, then reuse them for drafts, narration, voice previews, subtitles, and agent-driven production tasks. ```bash cast "Hello, world!" cast "Hello, world!" --voice-id tc_xxx --out hello.wav cast "Hello, world." --out hello.wav --timestamp-out hello.srt cast "I just got promoted!" --emotion smart ``` Install with Homebrew or Go, then authenticate with your Typecast API key. Convert text into playable audio or WAV/MP3 files with model, voice, and delivery controls. Generate timestamp alignment data, SRT, or WebVTT subtitles alongside audio. List, preview, pick, randomize, or run voice tournaments directly from the command line. Create a temporary custom voice from a WAV or MP3 sample and use it with the CLI. Store defaults in config, environment variables, or flags depending on your workflow. ## What you can repeat | Workflow | Command | |----------|---------| | Play speech immediately | `cast "Hello, world!"` | | Save a WAV file | `cast "Hello, world!" --out hello.wav` | | Save an MP3 file | `cast "Hello, world!" --out hello.mp3 --format mp3` | | Generate SRT captions | `cast "Hello, world." --out hello.wav --timestamp-out hello.srt` | | Generate WebVTT captions | `cast "Hello, world." --out hello.wav --timestamp-out hello.vtt --timestamp-format vtt` | | Use smart emotion | `cast "I can't believe it!" --emotion smart` | | Pick a voice interactively | `cast voices pick` | | Clone a custom voice | `cast voices clone sample.wav --name "My Clone"` | | Set a default voice | `cast config set voice-id tc_xxx` | The CLI requires a Typecast API key. Get one from the [Typecast API Console](https://studio.typecast.ai/developers/api). ## Next steps Set up the CLI and run your first synthesis command. Use the CLI in scripts, pipes, batch jobs, and content workflows. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Installation ## Install ```bash brew install neosapience/tap/cast ``` ```bash go install github.com/neosapience/cast@latest ``` ## Log in Run the login command and enter your Typecast API key when prompted: ```bash cast login ``` You can also pass the key directly: ```bash cast login ``` Get your key from the [Typecast API Console](https://studio.typecast.ai/developers/api). ## Verify the install ```bash cast "Hello, world!" ``` If you hear audio playback, CLI is ready. Use `cast "Hello, world!" --out hello.wav` if you want to verify file generation instead of local audio playback. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Synthesis ## Feature map | Need | Use | |------|-----| | Immediate local playback | `cast "text"` | | Reusable audio file | `--out file.wav` or `--out file.mp3 --format mp3` | | Realtime-feeling agent response | Default playback without `--out` | | Timestamp JSON | `--timestamp-out file.json` | | SRT or WebVTT subtitles | `--timestamp-out file.srt` or `--timestamp-out file.vtt` | | Custom cloned voice | `--voice-id uc_xxx` after `cast voices clone` | ## Basic usage ```bash # Play immediately cast "Hello, world!" # Use a specific voice cast "Hello, world!" --voice-id tc_xxx # Save to WAV file cast "Hello, world!" --out hello.wav # Save to MP3 file cast "Hello, world!" --out hello.mp3 --format mp3 # Save audio with SRT subtitles cast "Hello, world. This is a test." --out hello.wav --timestamp-out hello.srt ``` By default, `cast` plays audio immediately. Use `--out` to save a WAV or MP3 file instead. CLI's immediate playback is the fastest terminal workflow for local realtime feedback. For API-level chunked streaming (`POST /v1/text-to-speech/stream`), see [Streaming TTS](/quickstart#stream-audio-in-real-time) and the SDK docs. ## Options | Flag | Description | Default | |------|-------------|---------| | `--voice-id` | Voice ID | `tc_60e5426de8b95f1d3000d7b5` | | `--model` | Model (`ssfm-v30`, `ssfm-v21`) | `ssfm-v30` | | `--language` | Language code (ISO 639-3) | auto-detected | | `--emotion` | Emotion type: `smart`, `preset` | | | `--emotion-preset` | Preset emotion (requires `--emotion preset`) | | | `--emotion-intensity` | Emotion intensity 0.0-2.0 (requires `--emotion preset`) | `1.0` | | `--prev-text` | Previous sentence for context (`--emotion smart` only) | | | `--next-text` | Next sentence for context (`--emotion smart` only) | | | `--volume` | Volume (0-200) | `100` | | `--pitch` | Pitch in semitones (-12 to +12) | `0` | | `--tempo` | Tempo multiplier (0.5-2.0) | `1.0` | | `--remove-silence-ms` | Silence to retain (integer 0–1000 ms). 0 removes detected silence | unset | | `--format` | Output format (`wav`, `mp3`) | `wav` | | `--seed` | Unsigned integer seed for reproducible output (`>= 0`) | | | `--out` | Save to file instead of playing | | | `--timestamp-out` | Save timestamp output to JSON, SRT, or WebVTT | | | `--timestamp-format` | Timestamp output format (`json`, `srt`, `vtt`) | inferred from `--timestamp-out` | | `--timestamp-granularity` | Timestamp granularity (`word`, `char`, `both`) | server default | ## Models | Model | Languages | Emotions | Latency | |-------|-----------|----------|---------| | `ssfm-v30` | 35+ | 7 presets + smart emotion | Standard | | `ssfm-v21` | 27 | 4 presets: normal, happy, sad, angry | Low | ```bash cast "Hello, world!" --model ssfm-v21 ``` ## Emotions AI automatically infers the appropriate emotion from the text. Smart emotion is available with `ssfm-v30`. ```bash cast "I just got promoted!" --emotion smart ``` Provide surrounding sentences for better context: ```bash cast "I just got promoted!" --emotion smart \ --prev-text "I have been working so hard this year." \ --next-text "Let's celebrate tonight!" ``` Choose a specific emotion with `--emotion-preset`, and control its strength with `--emotion-intensity`. | Model | Available Presets | |-------|-------------------| | `ssfm-v30` | `normal`, `happy`, `sad`, `angry`, `whisper`, `toneup`, `tonedown` | | `ssfm-v21` | `normal`, `happy`, `sad`, `angry` | ```bash cast "Hello, world!" --emotion preset --emotion-preset happy cast "Hello, world!" --emotion preset --emotion-preset happy --emotion-intensity 2.0 cast "Hello, world!" --emotion preset --emotion-preset whisper --emotion-intensity 0.5 cast "Hello, world!" --model ssfm-v21 --emotion preset --emotion-preset sad ``` ## Control silence duration Use `--remove-silence-ms` with **Cast v1.0.10 or later**. The default is unset, not `0`. ```bash cast "Hello. Thank you for listening." --voice-id tc_672c5f5ce59fac2a48faeaee --remove-silence-ms 300 ``` `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Timestamps & captions The CLI can call Typecast Timestamp TTS and save alignment data alongside the generated audio. Use this when an agent needs subtitles for Shorts, caption timing for social video, karaoke-style highlights, or lip-sync metadata. ## Generate subtitles ```bash # Save audio and SRT subtitles cast "Hello, world. This is a test." \ --out hello.wav \ --timestamp-out hello.srt # Save audio and WebVTT subtitles cast "Hello, world. This is a test." \ --out hello.wav \ --timestamp-out hello.vtt \ --timestamp-format vtt ``` When `--timestamp-format` is omitted, CLI infers `srt` or `vtt` from the `--timestamp-out` extension and falls back to `json`. ## Save raw timestamp JSON ```bash cast "Hello, world. This is a test." \ --out hello.wav \ --timestamp-out hello.timestamps.json ``` JSON is useful when another tool will create captions, animate text, or align visuals manually. ## Choose granularity ```bash cast "Hello, world." \ --out hello.wav \ --timestamp-out hello.srt \ --timestamp-granularity both ``` For languages without whitespace between words, such as Japanese (`jpn`) or Chinese (`zho`), use character-level timestamps for usable subtitle timing: ```bash cast "こんにちは。世界。" \ --language jpn \ --out hello.wav \ --timestamp-out hello.srt ``` ## Caption workflow for agents ```text Create narration audio and captions from script.txt. Use the CLI. Write audio to ./video/voiceover.wav. Write subtitles to ./video/voiceover.srt. Keep the subtitle file next to the audio file. ``` ## Output choices | Output | Use when | |--------|----------| | `.srt` | Video editors, Shorts/Reels/TikTok caption import | | `.vtt` | Web video players and browser-based previews | | `.json` | Custom rendering, karaoke highlights, lip-sync, downstream automation | For social video, generate captions in the same step as audio. It keeps the final narration and subtitle timing tied to the exact same synthesis result. ## Control silence duration Use `--remove-silence-ms` with **Cast v1.0.10 or later**. The default is unset, not `0`. ```bash cast "Hello. Thank you for listening." --voice-id tc_672c5f5ce59fac2a48faeaee --remove-silence-ms 300 ``` `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Voices Use voice commands to find a stock voice, save a default voice, or create a custom cloned voice for a project. For cloning-specific examples, see [Voice cloning](/cli-reference/voice-cloning). ## Interactive picker Browse, preview, and select voices interactively: ```bash cast voices pick cast voices pick --gender female --age young_adult cast voices pick --text "Custom preview sentence" ``` | Key | Action | |-----|--------| | **P** | Preview with current model/emotion preset | | **E** | Preview with smart emotion (`ssfm-v30` only) | | **S** | Set as default voice | | **C** | Copy voice ID to clipboard | | **Enter** | Confirm and print voice ID | | **Esc** | Go back | ## Tournament Find your favorite voice through head-to-head elimination: ```bash cast voices tournament cast voices tournament --gender female --size 16 cast voices tournament --text "Custom preview sentence" ``` | Key | Action | |-----|--------| | **P** | Preview voice 1 | | **Q** | Preview voice 2 | | **1** | Pick voice 1 | | **2** | Pick voice 2 | ## Random voice Pick a random voice for experimentation: ```bash cast voices random cast voices random --gender female --age young_adult cast "Hello!" --voice-id $(cast voices random --model ssfm-v30 --gender female) ``` ## List and get voices List voices with filters: ```bash cast voices list cast voices list --gender female cast voices list --age young_adult cast voices list --model ssfm-v30 cast voices list --use-case Audiobook cast voices list --json ``` Available use cases: `Announcer`, `Anime`, `Audiobook`, `Conversational`, `Documentary`, `E-learning`, `Rapper`, `Game`, `Tiktok/Reels`, `News`, `Podcast`, `Voicemail`, `Ads` Get details for a specific voice: ```bash cast voices get ``` Recommend voices from a text description: ```bash cast voices recommend "warm female voice for product tutorials" cast voices recommend "calm narrator for meditation" --count 5 --json ``` Recommendation results contain only `voice_id`, `voice_name`, and `score`. Run `cast voices get ` or `cast voices list` when you need metadata such as supported models, emotions, gender, age, or use cases. ## Clone a voice ```bash cast voices clone sample.wav --name "My Clone" cast "Hello from my cloned voice." --voice-id uc_xxx --out cloned.wav cast voices delete uc_xxx ``` Use cloned voices when an agent needs a project-specific voice for review, narration drafts, or repeatable content generation. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Voice cloning Cast CLI v1.0.9 or later supports both Instant Voice Cloning and Professional Voice Cloning from a local WAV or MP3 sample. | Mode | Best for | Processing | | --------------- | ------------------------------------------ | ------------------------------------------------ | | Instant cloning | Fast previews and temporary project voices | Returns a ready-to-use voice ID immediately | | Professional Cloning | Higher-quality production voices | Trains asynchronously and requires status checks | ## Instant Voice Cloning ```bash cast voices clone sample.wav --name "My Clone" ``` The command prints the cloned voice ID by default, which makes it easy to use in scripts: ```bash voice_id=$(cast voices clone sample.wav --name "Review Clone") cast "Short test line." --voice-id "$voice_id" --out review.wav cast voices delete "$voice_id" ``` ## Use JSON output for handoff ```bash cast voices clone sample.mp3 --name "Review Clone" --json ``` Use JSON when an agent or another tool needs structured fields such as the cloned voice ID and suggested next-step values. ## Professional Voice Cloning Add `--professional` and the sample language code. The command prints a new `uc_` voice ID while training continues asynchronously. ```bash voice_id=$(cast voices clone sample.wav --name "My Premium Voice" \ --professional --language eng) cast voices clone status "$voice_id" ``` Check the status until it becomes `completed` or `failed`. Use `kor` for Korean samples and `eng` for English samples. ## Generate speech with the cloned voice ```bash cast "Hello from my cloned voice." \ --voice-id uc_xxx \ --emotion smart \ --out cloned.wav ``` ## Clean up cloned voices ```bash cast voices delete uc_xxx ``` ## Constraints | Constraint | Value | | --------------- | ---------------------------------------------- | | Input audio | WAV or MP3 | | Max file size | 25 MB | | Voice name | 1-30 characters | | Model | `ssfm-v30` | | Professional Cloning | `--professional` and `--language` are required | Treat cloned voices as project assets. Name them clearly, record which output files used them, and delete temporary clones when the workflow is done. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Configuration ## Set defaults Store default values so you do not have to pass flags every time: ```bash cast config set voice-id tc_xxx cast config set model ssfm-v21 cast config set volume 120 cast config list cast config unset volume ``` Available keys: `voice-id`, `model`, `language`, `emotion`, `emotion-preset`, `emotion-intensity`, `volume`, `pitch`, `tempo`, `format`, `remove-silence-ms` ## Resolution order Settings are resolved in this priority order: ```text --flag > environment variable > ~/.typecast/config.yaml > built-in default ``` ## Environment variables Any option can be set via environment variable using the `TYPECAST_` prefix: | Variable | Flag Equivalent | |----------|-----------------| | `TYPECAST_API_KEY` | `--api-key` | | `TYPECAST_VOICE_ID` | `--voice-id` | | `TYPECAST_MODEL` | `--model` | | `TYPECAST_LANGUAGE` | `--language` | | `TYPECAST_EMOTION` | `--emotion` | | `TYPECAST_EMOTION_PRESET` | `--emotion-preset` | | `TYPECAST_EMOTION_INTENSITY` | `--emotion-intensity` | | `TYPECAST_FORMAT` | `--format` | | `TYPECAST_VOLUME` | `--volume` | | `TYPECAST_PITCH` | `--pitch` | | `TYPECAST_TEMPO` | `--tempo` | | `TYPECAST_REMOVE_SILENCE_MS` | `--remove-silence-ms` | ## Control silence duration Use `--remove-silence-ms` with **Cast v1.0.10 or later**. The default is unset, not `0`. ```bash cast "Hello. Thank you for listening." --voice-id tc_672c5f5ce59fac2a48faeaee --remove-silence-ms 300 ``` `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. ```bash export TYPECAST_REMOVE_SILENCE_MS=300 cast config set remove-silence-ms 300 cast config unset remove-silence-ms ``` The environment variable is `TYPECAST_REMOVE_SILENCE_MS`; the YAML key is `remove_silence_ms`. Existing precedence applies: flag → environment → config file → default. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Recipes ```bash cast "$(cat script.txt)" ``` ```bash echo "System is ready." | cast cast "$(curl -s https://example.com/status.txt)" ``` ```bash cast "Chapter one." --out ch1.wav cast "Chapter two." --out ch2.wav cast "Chapter three." --out ch3.wav ``` ```bash cast "$(cat shorts-script.txt)" \ --out shorts-voiceover.wav \ --timestamp-out shorts-voiceover.srt ``` ```bash cast "$(cat preview-script.txt)" \ --out preview.wav \ --timestamp-out preview.vtt \ --timestamp-format vtt ``` ```bash voice_id=$(cast voices clone sample.wav --name "Draft Voice") cast "$(cat narration.txt)" --voice-id "$voice_id" --out draft.wav cast voices delete "$voice_id" ``` ```bash cast "It was a dark and stormy night." \ --emotion preset --emotion-preset normal --emotion-intensity 0.5 --out intro.wav cast "She opened the letter and gasped." \ --emotion preset --emotion-preset happy --emotion-intensity 1.5 --out climax.wav cast "He watched the train disappear into the fog." \ --emotion preset --emotion-preset sad --out farewell.wav ``` ```bash cast "I can't believe we actually made it!" --emotion smart \ --prev-text "We've been working on this for three years." \ --next-text "Let's celebrate tonight!" ``` ```bash cast "Hello, world!" --seed 42 --out hello.wav cast "Hello, world!" --seed 42 --out hello2.wav ``` ```bash cast "Bonjour le monde" --language fra cast "Hola mundo" --language spa ``` ```bash cast "Buy now, limited time offer!" --tempo 1.3 --pitch 2 cast "Relax and take a deep breath." --tempo 0.85 --volume 90 ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Troubleshooting - Verify the installation completed successfully. - Homebrew: run `brew list neosapience/tap/cast`. - Go: ensure `$GOPATH/bin` is in your `PATH`. - Open a new terminal session after installing. - Run `cast login` to re-enter your API key. - Verify your key in the [Typecast API Console](https://studio.typecast.ai/developers/api). - Run `cast logout`, then `cast login` to reset local credentials. - Save to a file instead: `cast "test" --out test.wav`. - Check your system audio output device. - Ensure your system volume is not muted. ## Resources Source code and releases. Explore the Typecast API. Browse available voices. Manage API keys and settings. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Shorts generation Short-form video work usually needs fast iteration: a hook, a few alternate reads, and a final audio file that can be dropped into an editor. The CLI is useful because an agent can turn script drafts into named audio assets without switching tools. ## Recommended flow Keep each line short enough for captions and editing. ```text Write a 25-second Shorts script with: - one opening hook - three short beats - one call to action ``` ```bash cast "This is the fastest way to test a Typecast voice from your terminal." \ --emotion smart \ --out shorts-scratch.wav ``` ```bash cast "Stop scrolling. Your app can speak in one command." \ --emotion preset --emotion-preset happy --emotion-intensity 1.3 \ --out hook-a.wav cast "Here is a terminal trick for instant AI voiceovers." \ --emotion smart \ --out hook-b.wav ``` ```bash cast "$(cat shorts-script.txt)" \ --voice-id tc_xxx \ --emotion smart \ --format mp3 \ --out shorts-final.mp3 ``` ```bash cast "$(cat shorts-script.txt)" \ --voice-id tc_xxx \ --emotion smart \ --out shorts-final.wav \ --timestamp-out shorts-final.srt ``` ## Agent prompt pattern ```text Create a short-form video voiceover. Write the script first, then generate: 1. hook-a.wav 2. hook-b.wav 3. final.mp3 4. final.srt Use the CLI. Keep filenames descriptive, generate captions from the same final script, and do not overwrite approved takes. ``` ## Practical tips | Goal | CLI option | |------|-----------------| | Faster delivery | `--tempo 1.08` to `--tempo 1.18` | | More energetic read | `--emotion preset --emotion-preset happy --emotion-intensity 1.2` | | More natural context | `--emotion smart --prev-text ... --next-text ...` | | Editor-friendly output | `--out final.mp3 --format mp3` | | Subtitle import | `--timestamp-out final.srt` | | Web preview captions | `--timestamp-out final.vtt --timestamp-format vtt` | | Custom campaign voice | `cast voices clone sample.wav --name "Campaign Voice"` | | Reproducible drafts | `--seed 42` | ## What to automate | Shorts task | Recommended CLI feature | |-------------|------------------------------| | Opening hook A/B tests | Separate `hook-a.wav`, `hook-b.wav` files | | Captions for editing | `--timestamp-out final.srt` | | Browser preview captions | `--timestamp-out final.vtt --timestamp-format vtt` | | Branded or creator voice | `cast voices clone` then use `--voice-id uc_xxx` | | Fast review in a room | Run `cast "short sentence"` without `--out` for immediate playback | Generate hooks as separate files. It makes it easier for the agent or editor to compare openings without regenerating the full narration. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Audiobook generation Audiobook workflows benefit from repeatable naming, stable voice settings, and chapter-level files. The CLI gives an agent a simple command surface for generating drafts, rerendering chapters, and keeping approved audio separate. ## Prepare defaults Set the voice and model once before generating chapters: ```bash cast config set voice-id tc_xxx cast config set model ssfm-v30 cast config set format mp3 ``` If you need a project-specific voice, clone it first and store the returned `uc_` voice ID: ```bash cast voices clone narrator-sample.wav --name "Narrator Draft" cast config set voice-id uc_xxx ``` ## Generate chapters ```bash cast "$(cat chapter-01.txt)" \ --emotion smart \ --out audiobook/chapter-01.mp3 \ --format mp3 cast "$(cat chapter-02.txt)" \ --emotion smart \ --out audiobook/chapter-02.mp3 \ --format mp3 ``` ## Generate captions or review timing ```bash cast "$(cat chapter-01.txt)" \ --emotion smart \ --out audiobook/chapter-01.wav \ --timestamp-out audiobook/chapter-01.timestamps.json ``` Use JSON for detailed review timing, or write `.srt` / `.vtt` when the audiobook content also needs a video preview. ## Use emotion for scene changes For short passages with a known tone, preset emotion can be more controllable than smart emotion: ```bash cast "The room fell silent as the letter slipped from her hand." \ --emotion preset \ --emotion-preset sad \ --emotion-intensity 1.2 \ --out audiobook/scene-letter.mp3 ``` For passages where surrounding context matters, pass neighboring text: ```bash cast "She opened the door and froze." \ --emotion smart \ --prev-text "The hallway had been empty a moment ago." \ --next-text "A familiar voice whispered her name." \ --out audiobook/scene-door.mp3 ``` ## Agent prompt pattern ```text Generate audiobook draft files from the chapter text files. Use the same voice for every chapter. Write output to ./audiobook. Use one MP3 per chapter. For chapters that also need video previews, generate an SRT file next to the audio. If a chapter fails, report the filename and keep going. ``` ## File naming | Asset | Suggested filename | |-------|--------------------| | Full chapter | `chapter-01.mp3` | | Scene revision | `chapter-01-scene-03-v2.mp3` | | Approved final | `chapter-01-final.mp3` | | Alternate delivery | `chapter-01-alt-happy.mp3` | ## When to use advanced features | Need | Recommended feature | |------|---------------------| | Consistent narrator identity | `cast config set voice-id ...` | | Temporary narrator matching a sample | `cast voices clone narrator-sample.wav --name "Narrator Draft"` | | Chapter review timing | `--timestamp-out chapter-01.timestamps.json` | | Video preview for a chapter | `--timestamp-out chapter-01.srt` | | Quick approval playback | `cast "one review sentence"` without `--out` | Avoid overwriting approved audio. Ask the agent to write revisions with `-v2`, `-v3`, or a delivery label. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Agent skill integration The CLI works well inside agent skills because it exposes Typecast speech generation as a shell command. The agent can draft text, choose filenames, call `cast`, and return playable audio assets. ## Skill instruction template ```markdown # Typecast speech skill Use the CLI when the user asks for voiceover, narration, dialogue, or audio preview. Rules: - Never expose the API key. - Prefer `--out` so generated audio is saved as a file. - Use descriptive filenames. - Use `--emotion smart` for natural narration. - Use preset emotion when the user specifies a tone. - Use `--timestamp-out` when the user asks for captions, subtitles, timing, or lip-sync. - Use `cast voices clone` only when the user provides or approves a voice sample. - Report the output path after generation. ``` ## Minimal command set | Task | Command | |------|---------| | Check authentication | `cast "test" --out typecast-test.wav` | | Generate narration | `cast "$(cat script.txt)" --emotion smart --out narration.wav` | | Generate MP3 | `cast "$(cat script.txt)" --format mp3 --out narration.mp3` | | Generate captions | `cast "$(cat script.txt)" --out narration.wav --timestamp-out narration.srt` | | Pick a voice | `cast voices pick` | | Clone a voice | `cast voices clone sample.wav --name "Project Voice"` | | Save a default voice | `cast config set voice-id tc_xxx` | ## Agent prompt pattern ```text Use the Typecast speech skill. Create three voiceover takes from script.txt: - neutral - energetic - soft Save them under ./voiceover and tell me the filenames. ``` ## Capability routing Teach the agent to choose the smallest CLI feature that satisfies the request: | User asks for | Agent should use | |---------------|------------------| | "Say this out loud" | `cast "..."` without `--out` | | "Make a voiceover file" | `cast "$(cat script.txt)" --out narration.wav` | | "Make captions too" | Add `--timestamp-out narration.srt` | | "I want this sample voice" | `cast voices clone sample.wav --name ...` then `--voice-id uc_xxx` | | "Preview a few voices" | `cast voices pick` or `cast voices tournament` | ## Recommended safeguards Store the API key with `cast login` or `TYPECAST_API_KEY`. Do not paste the key into a shared prompt or generated document. Prefer `cast "$(cat script.txt)"` for scripts that are too long to safely quote in a single command. Tell the agent to create new filenames for revisions instead of overwriting audio that has already been reviewed. Use Typecast with Claude Skills and agent workflows. Set default voice, model, format, and environment variables. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # PC audio output When an agent controls a local computer, the CLI can turn short text into speech and play it through the default audio output. This is useful for audible alerts, hands-free status updates, or quick voice previews. For local agent feedback, `cast "message"` is the simplest realtime-style path because playback starts as part of the CLI command. For API-level chunked streaming, use the Typecast streaming endpoint through an SDK. ## Speak immediately ```bash cast "The export is complete." ``` By default, the CLI plays the generated audio instead of saving it. ## Use saved audio for reliability For longer messages or repeated playback, save the audio first: ```bash cast "The build failed. Check the test report before pushing." --out agent-alert.wav ``` Then play it with your system audio tool: ```bash afplay agent-alert.wav ``` ```bash aplay agent-alert.wav ``` ## Agent prompt pattern ```text When a long-running task finishes, speak a short status update through PC audio. Use the CLI. Keep the spoken sentence under 12 words. If playback fails, save the audio file and report its path. ``` ## Good audio messages | Situation | Suggested text | |-----------|----------------| | Task complete | `The task is complete.` | | Human input needed | `I need your input to continue.` | | Test failure | `Tests failed. Please check the report.` | | Deployment ready | `The preview is ready.` | ## Choosing the audio path | Need | Best path | |------|-----------| | Fast audible status | `cast "The preview is ready."` | | Repeat the same alert | Save with `--out agent-alert.wav`, then replay with `afplay` | | Agent-specific voice | Set `cast config set voice-id tc_xxx` | | Project-specific voice | Clone once with `cast voices clone`, then use `--voice-id uc_xxx` | | Detailed report | Speak one sentence, write details in chat or a log | Keep PC audio messages short. For detailed status, speak one sentence and write the full details in the chat or log. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Overview Typecast is an AI-powered text-to-speech platform for creating natural voice narration. It provides a wide range of synthetic voices and converts text into speech across multiple languages. **Building with an AI agent?** Copy the prompt below and paste it into your agent. ```text Read https://typecast.ai/docs/llms.txt first, then help me build with the Typecast API. ``` New to Typecast? Use the **Ask Assistant** button at the top of the Typecast docs. Ask questions like "Which SDK should I use for a Next.js app?", "How do I stream audio?", or "Show me the fastest way to generate captions." ## What Typecast API provides Generate speech in Korean, English, Japanese, Chinese, Spanish, Vietnamese, and many more languages with the ssfm-v30 model. Choose voices by model, gender, age group, and use case, or create a custom voice with instant cloning. Control emotion, pacing, format, and language while using consistent voice metadata across API and SDK flows. ## Core API features Use Typecast API for full audio generation, real-time playback, subtitle timing, and custom voice creation. Convert text into complete WAV or MP3 audio files for apps, videos, narration, learning content, and voice products. Play audio as chunks arrive instead of waiting for the full synthesis result. Useful for voice agents, interactive apps, and low-latency playback. Generate audio with word- or character-level alignment data for subtitles, karaoke highlights, and lip-sync. Create a custom voice from a short audio sample and use it in text-to-speech requests alongside Typecast-provided voices. ## Choose how to integrate After you understand what Typecast provides, choose the workflow that matches how you want to build. Let Claude, Cursor, OpenClaw, or another agent read the Typecast docs and generate integration code for you. Prefer the official SDK for your language instead of asking the agent to hand-roll raw HTTP calls. Follow the quickstart, create an API key, pick a voice, and run your first TTS request. Use the API Reference when you need exact request and response fields. ## Direct implementation Create an API key and generate your first audio file. Use Python, JavaScript, Go, Rust, C#, Java, Kotlin, C, Swift, Zig, PHP, Dart, or Ruby. Check exact endpoints, request parameters, response schemas, and Try It examples. ## AI-assisted development If you are asking an AI agent to add Typecast to your project, give it one of these docs first: Best for Claude Code and Claude Desktop. The agent gets task-specific Typecast instructions and examples. Best when your agent can connect to remote MCP docs or a self-hosted Typecast MCP server. Best for local agent workflows that can run shell commands, use the cast CLI, or connect MCP tools. Prompt your agent to use the official SDK page for your language first. SDKs include helpers for text-to-speech, streaming, timestamp TTS, subtitle export, voice lookup, and error handling. ## No-code tools If you want to automate voice generation without writing a full app, start with the integration that matches your workflow: Trigger Typecast voice generation from thousands of apps and automate handoffs between tools. Build visual scenarios for repeatable TTS pipelines, content workflows, and multilingual production. Create self-hosted or cloud automation workflows that call Typecast as one step in a larger process. Generate audio from spreadsheet rows for batch jobs, team operations, and content lists. ## Common next links Check exact endpoints, request parameters, response schemas, and Try It examples. Browse available voices and choose the voice ID for your request. Check plan limits, pricing, and credit usage before you ship or scale. Compare ssfm-v30 and ssfm-v21 language support, emotion controls, and model behavior. ## Next step Start with the [Quickstart](/quickstart), or open **Ask Assistant** and describe your app, language, and target feature. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Quickstart ## Get Started with Authentication To use the Typecast API, you'll need to authenticate your requests with an API key. Follow these steps: Visit your [Typecast API Console](https://studio.typecast.ai/developers/api) to generate a new API key Keep your API key secure - we recommend storing it as an environment variable Python SDK 0.4.0 supports Python 3.10 through 3.14. **Python 3.8 and 3.9 are no longer supported because they have reached end of life (EOL).** The last compatible SDK is **typecast-python 0.3.15**. Upgrade Python first. See [supported versions and migration guidance](/sdk/python). ## Make your first request ```bash Python pip install --upgrade typecast-python ``` ```bash Javascript npm install @neosapience/typecast-js # pnpm add @neosapience/typecast-js # yarn add @neosapience/typecast-js ``` ```bash C#/.NET dotnet add package typecast-csharp ``` ```xml Java (Maven) com.neosapience typecast-java 1.2.12 ``` ```kotlin Kotlin (Gradle) dependencies { implementation("com.neosapience:typecast-kotlin:1.2.13") } ``` ```toml Rust (Cargo.toml) [dependencies] typecast-rust = "0.3.15" tokio = { version = "1", features = ["full"] } ``` ```bash Go go get github.com/neosapience/typecast-sdk/typecast-go ``` All SDKs require the latest version. - **Python**: If you have an older version, upgrade with `pip install --upgrade typecast-python` - **Javascript**: If you have an older version, upgrade with `npm update @neosapience/typecast-js` - **C#**: Update with `dotnet add package typecast-csharp` - **Java**: Update the version in your `pom.xml` or `build.gradle` - **Kotlin**: Update the version in your `build.gradle.kts` - **Rust**: Update the version in your `Cargo.toml` If you only need to synthesize speech and save an audio file, use the SDK's `generateToFile` or `generate_to_file` helper. Each SDK page includes a language-specific example. ```python Python from typecast import Typecast from typecast.models import TTSRequest, SmartPrompt # Initialize client client = Typecast(api_key="YOUR_API_KEY") # Convert text to speech response = client.text_to_speech(TTSRequest( text="Everything is going to be okay.", model="ssfm-v30", voice_id="tc_672c5f5ce59fac2a48faeaee", prompt=SmartPrompt( emotion_type="smart", previous_text="I just got the best news!", next_text="I can't wait to celebrate!" ) )) # Save audio file with open('typecast.wav', 'wb') as f: f.write(response.audio_data) ``` ```javascript Javascript import { TypecastClient } from '@neosapience/typecast-js'; import fs from 'fs'; // Initialize client const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' }); // Convert text to speech const audio = await client.textToSpeech({ text: "Everything is going to be okay.", model: "ssfm-v30", voice_id: "tc_672c5f5ce59fac2a48faeaee", prompt: { emotion_type: "smart", previous_text: "I just got the best news!", next_text: "I can't wait to celebrate!" } }); // Save audio file await fs.promises.writeFile('typecast.wav', Buffer.from(audio.audioData)); ``` ```csharp C# using Typecast; using Typecast.Models; // Initialize client using var client = new TypecastClient("YOUR_API_KEY"); // Convert text to speech var request = new TTSRequest( text: "Everything is going to be okay.", voiceId: "tc_672c5f5ce59fac2a48faeaee", model: TTSModel.SsfmV30 ) { Prompt = new SmartPrompt( previousText: "I just got the best news!", nextText: "I can't wait to celebrate!" ) }; var response = await client.TextToSpeechAsync(request); // Save audio file await response.SaveToFileAsync("typecast.wav"); ``` ```java Java import com.neosapience.TypecastClient; import com.neosapience.models.*; import java.io.FileOutputStream; // Initialize client TypecastClient client = new TypecastClient("YOUR_API_KEY"); // Convert text to speech TTSRequest request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("Everything is going to be okay.") .model(TTSModel.SSFM_V30) .prompt(SmartPrompt.builder() .previousText("I just got the best news!") .nextText("I can't wait to celebrate!") .build()) .build(); TTSResponse response = client.textToSpeech(request); // Save audio file try (FileOutputStream fos = new FileOutputStream("typecast.wav")) { fos.write(response.getAudioData()); } client.close(); ``` ```kotlin Kotlin import com.neosapience.TypecastClient import com.neosapience.models.* import java.io.File // Initialize client val client = TypecastClient.create("YOUR_API_KEY") // Convert text to speech val request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("Everything is going to be okay.") .model(TTSModel.SSFM_V30) .prompt(SmartPrompt.builder() .previousText("I just got the best news!") .nextText("I can't wait to celebrate!") .build()) .build() val response = client.textToSpeech(request) // Save audio file File("typecast.wav").writeBytes(response.audioData) client.close() ``` ```rust Rust use typecast_rust::{TypecastClient, TTSRequest, TTSModel, SmartPrompt}; use std::fs; #[tokio::main] async fn main() -> Result<(), Box> { // Initialize client let client = TypecastClient::with_api_key("YOUR_API_KEY")?; // Convert text to speech let request = TTSRequest::new( "tc_672c5f5ce59fac2a48faeaee", "Everything is going to be okay.", TTSModel::SsfmV30, ) .prompt( SmartPrompt::new() .previous_text("I just got the best news!") .next_text("I can't wait to celebrate!") ); let response = client.text_to_speech(&request).await?; // Save audio file fs::write("typecast.wav", &response.audio_data)?; Ok(()) } ``` You can set up your API key in two ways: {/* - Add it to your `.env` file */} - Configure it directly in your application code - Set it as a shell environment variable ```bash Shell (Linux/macOS) # Set for current session export TYPECAST_API_KEY='YOUR_API_KEY' ``` ```bash Shell (Windows) # Set for current session set TYPECAST_API_KEY=YOUR_API_KEY ``` {/* ```bash .env TYPECAST_API_KEY = 'YOUR_API_KEY'; ``` */} ```python Python import requests import os api_key = os.environ.get("TYPECAST_API_KEY", "YOUR_API_KEY") url = "https://api.typecast.ai/v1/text-to-speech" headers = {"X-API-KEY": api_key, "Content-Type": "application/json"} payload = { "text": "Everything is going to be okay.", "model": "ssfm-v30", "voice_id": "tc_672c5f5ce59fac2a48faeaee", "prompt": { "emotion_type": "smart", "previous_text": "I just got the best news!", "next_text": "I can't wait to celebrate!" } } response = requests.post(url, headers=headers, json=payload) if response.status_code == 200: with open('typecast.wav', 'wb') as f: f.write(response.content) print("Audio file saved as typecast.wav") else: print(f"Error: {response.status_code} - {response.text}") ``` ```javascript Javascript import fs from "fs"; const apiKey = process.env.TYPECAST_API_KEY || 'YOUR_API_KEY'; async function convertTextToSpeech() { const url = 'https://api.typecast.ai/v1/text-to-speech'; const payload = { text: "Everything is going to be okay.", model: "ssfm-v30", voice_id: "tc_672c5f5ce59fac2a48faeaee", prompt: { emotion_type: "smart", previous_text: "I just got the best news!", next_text: "I can't wait to celebrate!" } }; try { const response = await fetch(url, { method: 'POST', headers: { 'X-API-KEY': apiKey, 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (!response.ok) { throw new Error(`Error: ${response.status} - ${await response.text()}`); } const audioData = await response.arrayBuffer(); fs.writeFileSync('typecast.wav', Buffer.from(audioData)); console.log('Audio file saved as typecast.wav'); } catch (error) { console.error('Error converting text to speech:', error); } } convertTextToSpeech(); ``` ```java Java import java.io.*; import java.net.HttpURLConnection; import java.net.URL; String apiKey = System.getenv("TYPECAST_API_KEY"); if (apiKey == null) apiKey = "YOUR_API_KEY"; String urlString = "https://api.typecast.ai/v1/text-to-speech"; String payload = "{" + "\"text\": \"Everything is going to be okay.\"," + "\"model\": \"ssfm-v30\"," + "\"voice_id\": \"tc_672c5f5ce59fac2a48faeaee\"," + "\"prompt\": {" + "\"emotion_type\": \"smart\"," + "\"previous_text\": \"I just got the best news!\"," + "\"next_text\": \"I can't wait to celebrate!\"" + "}" + "}"; URL url = new URL(urlString); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("X-API-KEY", apiKey); conn.setRequestProperty("Content-Type", "application/json"); conn.setDoOutput(true); try (OutputStream os = conn.getOutputStream()) { os.write(payload.getBytes("UTF-8")); } if (conn.getResponseCode() == 200) { try (InputStream is = conn.getInputStream(); FileOutputStream fos = new FileOutputStream("typecast.wav")) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = is.read(buffer)) != -1) { fos.write(buffer, 0, bytesRead); } } System.out.println("Audio file saved as typecast.wav"); } else { System.out.println("Error: " + conn.getResponseCode()); } ``` ```csharp C# using System.Net.Http.Headers; using System.Text; using System.Text.Json; var apiKey = Environment.GetEnvironmentVariable("TYPECAST_API_KEY") ?? "YOUR_API_KEY"; using var client = new HttpClient(); client.DefaultRequestHeaders.Add("X-API-KEY", apiKey); var payload = new { text = "Everything is going to be okay.", model = "ssfm-v30", voice_id = "tc_672c5f5ce59fac2a48faeaee", prompt = new { emotion_type = "smart", previous_text = "I just got the best news!", next_text = "I can't wait to celebrate!" } }; var content = new StringContent( JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json" ); var response = await client.PostAsync("https://api.typecast.ai/v1/text-to-speech", content); if (response.IsSuccessStatusCode) { var audioData = await response.Content.ReadAsByteArrayAsync(); await File.WriteAllBytesAsync("typecast.wav", audioData); Console.WriteLine("Audio file saved as typecast.wav"); } else { Console.WriteLine($"Error: {response.StatusCode}"); } ``` ```bash cURL curl -X POST "https://api.typecast.ai/v1/text-to-speech" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "ssfm-v30", "text": "Everything is going to be okay.", "voice_id": "tc_672c5f5ce59fac2a48faeaee", "prompt": { "emotion_type": "smart", "previous_text": "I just got the best news!", "next_text": "I can't wait to celebrate!" } }' --output typecast.wav ``` ### Audio Output Settings You can customize the audio output by adding an `output` object to your request: | Parameter | Type | Range | Default | Description | |-----------|------|-------|---------|-------------| | `volume` | integer | 0–200 | 100 | Relative volume scaling. Cannot be used with `target_lufs`. | | `target_lufs` | number | -70–0 | - | Absolute loudness normalization in LUFS. Cannot be used with `volume`. | | `audio_pitch` | integer | -12–12 | 0 | Pitch adjustment in semitones. | | `audio_tempo` | number | 0.5–2.0 | 1.0 | Speed multiplier. | | `audio_format` | string | wav, mp3 | wav | Output audio format. | | `remove_silence_ms` | integer / null | 0–1000 | null | Silence to retain (ms). 0 removes detected silence; omitted/null disables processing. | Use `target_lufs` for consistent loudness across different clips. Use `volume` for simple relative scaling. ```json Example: output with target_lufs { "text": "Consistent loudness example.", "model": "ssfm-v30", "voice_id": "tc_672c5f5ce59fac2a48faeaee", "output": { "target_lufs": -14.0, "audio_format": "mp3" } } ``` To browse and select available voice IDs for your requests, refer to [List Voices](/docs/api-reference/voices/list-voices) in the API Reference. ## List all voices To use Typecast effectively, you need access to voice IDs. The current `/v3/voices` endpoint provides available voices with localized names, supported models, emotions, and preview metadata. You can filter voices by model, gender, age, and use cases using optional query parameters. You can preview available API voices and listen to sample audio without making an API call on the [Voices](https://studio.typecast.ai/developers/api/voices) page. Use it to compare voices first, then copy the Voice ID into your API request. ```python Python from typecast import Typecast from typecast.models import VoicesV2Filter, TTSModel # Initialize client client = Typecast(api_key="YOUR_API_KEY") # Get all voices (optionally filter by model, gender, age, use_cases) voices = client.voices_v3(VoicesV2Filter(model=TTSModel.SSFM_V30)) print(f"Found {len(voices)} voices:") for voice in voices: for model in voice.models: print(f"ID: {voice.voice_id}, Name: {voice.voice_name.eng}, Model: {model.version.value}, Emotions: {', '.join(model.emotions)}") ``` ```javascript Javascript import { TypecastClient } from '@neosapience/typecast-js'; // Initialize client const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' }); // Get all voices (optionally filter by model, gender, age, use_cases) const voices = await client.getVoicesV3({model: 'ssfm-v30'}); console.log(`Found ${voices.length} voices:`); voices.forEach(voice => { voice.models.forEach(model => { console.log(`ID: ${voice.voice_id}, Name: ${voice.voice_name.eng}, Model: ${model.version}, Emotions: ${model.emotions.join(', ')}`); }); }); ``` ```csharp C# using Typecast; using Typecast.Models; // Initialize client using var client = new TypecastClient("YOUR_API_KEY"); // Get all voices (optionally filter by model, gender, age, use_cases) var filter = new VoicesV2Filter { Model = TTSModel.SsfmV30 }; var voices = await client.GetVoicesV3Async(filter); Console.WriteLine($"Found {voices.Count} voices:"); foreach (var voice in voices) { foreach (var model in voice.Models) { Console.WriteLine($"ID: {voice.VoiceId}, Name: {voice.VoiceName.Eng}, Model: {model.Version}, Emotions: {string.Join(", ", model.Emotions)}"); } } ``` ```java Java import com.neosapience.TypecastClient; import com.neosapience.models.*; // Initialize client TypecastClient client = new TypecastClient("YOUR_API_KEY"); // Get all voices (optionally filter by model, gender, age, use_cases) VoicesV2Filter filter = VoicesV2Filter.builder() .model(TTSModel.SSFM_V30) .build(); List voices = client.getVoicesV3(filter); System.out.println("Found " + voices.size() + " voices:"); for (VoiceV3Response voice : voices) { for (ModelInfo model : voice.getModels()) { System.out.println("ID: " + voice.getVoiceId() + ", Name: " + voice.getVoiceName().eng + ", Model: " + model.getVersion() + ", Emotions: " + String.join(", ", model.getEmotions())); } } client.close(); ``` ```python Python import requests import os api_key = os.environ.get("TYPECAST_API_KEY", "YOUR_API_KEY") url = "https://api.typecast.ai/v3/voices" headers = {"X-API-KEY": api_key} params = {"model": "ssfm-v30"} # Optional: model, gender, age, use_cases response = requests.get(url, headers=headers, params=params) if response.status_code == 200: voices = response.json() print(f"Found {len(voices)} voices:") for voice in voices: for model in voice['models']: print(f"ID: {voice['voice_id']}, Name: {voice['voice_name']['eng']}, Model: {model['version']}, Emotions: {', '.join(model['emotions'])}") else: print(f"Error: {response.status_code} - {response.text}") ``` ```javascript Javascript const apiKey = process.env.TYPECAST_API_KEY || 'YOUR_API_KEY'; async function getVoices() { const url = 'https://api.typecast.ai/v3/voices'; const params = new URLSearchParams({model: 'ssfm-v30'}); // Optional: model, gender, age, use_cases try { const response = await fetch(`${url}?${params}`, { method: 'GET', headers: {'X-API-KEY': apiKey} }); if (!response.ok) { throw new Error(`Error: ${response.status} - ${await response.text()}`); } const voices = await response.json(); console.log(`Found ${voices.length} voices:`); voices.forEach(voice => { voice.models.forEach(model => { console.log(`ID: ${voice.voice_id}, Name: ${voice.voice_name.eng}, Model: ${model.version}, Emotions: ${model.emotions.join(', ')}`); }); }); } catch (error) { console.error('Error fetching voices:', error); } } getVoices(); ``` ```java Java import java.io.*; import java.net.HttpURLConnection; import java.net.URL; String apiKey = System.getenv("TYPECAST_API_KEY"); if (apiKey == null) apiKey = "YOUR_API_KEY"; String urlString = "https://api.typecast.ai/v3/voices?model=ssfm-v30"; URL url = new URL(urlString); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("X-API-KEY", apiKey); if (conn.getResponseCode() == 200) { try (BufferedReader br = new BufferedReader( new InputStreamReader(conn.getInputStream(), "UTF-8"))) { StringBuilder response = new StringBuilder(); String line; while ((line = br.readLine()) != null) { response.append(line); } System.out.println(response.toString()); } } else { System.out.println("Error: " + conn.getResponseCode()); } ``` ```csharp C# var apiKey = Environment.GetEnvironmentVariable("TYPECAST_API_KEY") ?? "YOUR_API_KEY"; using var client = new HttpClient(); client.DefaultRequestHeaders.Add("X-API-KEY", apiKey); var response = await client.GetAsync("https://api.typecast.ai/v3/voices?model=ssfm-v30"); if (response.IsSuccessStatusCode) { var json = await response.Content.ReadAsStringAsync(); Console.WriteLine(json); } else { Console.WriteLine($"Error: {response.StatusCode}"); } ``` ```bash cURL curl -X GET "https://api.typecast.ai/v3/voices?model=ssfm-v30" \ -H "X-API-KEY: YOUR_API_KEY" ``` The response will be a JSON array of voice objects, each containing: ```json { "voice_id": "tc_672c5f5ce59fac2a48faeaee", "voice_name": {"eng": "Dylan", "kor": "딜런"}, "models": [ { "version": "ssfm-v30", "emotions": ["normal", "happy", "sad", "angry", "whisper", "toneup", "tonedown"] } ], "gender": "male", "age": "young_adult", "use_cases": ["Conversational", "TikTok/Reels/Shorts", "Audiobook/Storytelling"], "voice_type": "original", "preview_url": "https://..." } ``` You'll need a valid voice ID when making text-to-speech requests. With ssfm-v30, all 7 emotion presets are available across all voices. ## Stream audio in real time For low-latency applications, use the streaming endpoint to play audio as chunks arrive - no need to wait for full synthesis. **WAV streaming format:** 32000 Hz, 16-bit, mono PCM. The first chunk includes a 44-byte WAV header; subsequent chunks are raw PCM only. ```python Python # pip install typecast-python sounddevice import sounddevice as sd from typecast import Typecast from typecast.models import TTSRequestStream, OutputStream client = Typecast(api_key="YOUR_API_KEY") request = TTSRequestStream( text="Stream this text as audio in real time.", model="ssfm-v30", voice_id="tc_672c5f5ce59fac2a48faeaee", output=OutputStream(audio_format="wav", target_lufs=-14.0) ) with sd.RawOutputStream(samplerate=32000, channels=1, dtype="int16") as player: buf, first = bytearray(), True for chunk in client.text_to_speech_stream(request): if first: chunk = chunk[44:] # Skip 44-byte WAV header first = False buf.extend(chunk) n = len(buf) - (len(buf) % 2) # int16 alignment if n: player.write(bytes(buf[:n])) del buf[:n] ``` ```javascript Javascript // Node 18+. Pipe stream to ffplay for real-time playback. // Prerequisite: ffmpeg (brew/choco/apt install ffmpeg) import { spawn } from "node:child_process"; import { TypecastClient } from '@neosapience/typecast-js'; const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' }); const ffplay = spawn( "ffplay", ["-autoexit", "-nodisp", "-loglevel", "error", "-i", "pipe:0"], { stdio: ["pipe", "ignore", "ignore"] }, ); const stream = await client.textToSpeechStream({ text: "Stream this text as audio in real time.", model: "ssfm-v30", voice_id: "tc_672c5f5ce59fac2a48faeaee", output: { audio_format: "wav", target_lufs: -14.0 } }); const reader = stream.getReader(); while (true) { const { value, done } = await reader.read(); if (done) break; ffplay.stdin.write(value); } ffplay.stdin.end(); await new Promise((resolve) => ffplay.on("close", resolve)); ``` ```java Java import com.neosapience.TypecastClient; import com.neosapience.models.*; import javax.sound.sampled.*; import java.io.*; TypecastClient client = new TypecastClient("YOUR_API_KEY"); TTSRequestStream request = TTSRequestStream.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("Stream this text as audio in real time.") .model(TTSModel.SSFM_V30) .output(com.neosapience.models.OutputStream.builder() .audioFormat(AudioFormat.WAV) .targetLufs(-14.0).build()) .build(); // 32000 Hz, 16-bit, mono, signed, little-endian javax.sound.sampled.AudioFormat format = new javax.sound.sampled.AudioFormat(32000, 16, 1, true, false); SourceDataLine line = javax.sound.sampled.AudioSystem.getSourceDataLine(format); line.open(format, 8192); line.start(); try (InputStream stream = client.textToSpeechStream(request)) { byte[] buf = new byte[4096]; boolean first = true; int bytesRead; while ((bytesRead = stream.read(buf)) != -1) { int offset = 0; if (first) { offset = 44; bytesRead -= 44; first = false; } line.write(buf, offset, bytesRead); } } line.drain(); line.close(); client.close(); ``` See each [SDK documentation](/sdk/python) for more languages (Go, Rust, Swift, C#, Kotlin, C) with real-time playback examples. ```python Python # pip install requests sounddevice import requests import sounddevice as sd import os api_key = os.environ.get("TYPECAST_API_KEY", "YOUR_API_KEY") response = requests.post( "https://api.typecast.ai/v1/text-to-speech/stream", headers={"X-API-KEY": api_key, "Content-Type": "application/json"}, json={ "text": "Stream this text as audio in real time.", "model": "ssfm-v30", "voice_id": "tc_672c5f5ce59fac2a48faeaee", "output": {"audio_format": "wav", "target_lufs": -14.0}, }, stream=True ) response.raise_for_status() with sd.RawOutputStream(samplerate=32000, channels=1, dtype="int16") as player: buf, first = bytearray(), True for chunk in response.iter_content(chunk_size=4096): if not chunk: continue if first: chunk = chunk[44:] # Skip WAV header first = False buf.extend(chunk) n = len(buf) - (len(buf) % 2) if n: player.write(bytes(buf[:n])) del buf[:n] ``` ```bash cURL + ffplay # Pipe directly to ffplay for instant playback curl -s -X POST "https://api.typecast.ai/v1/text-to-speech/stream" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "ssfm-v30", "text": "Stream this text as audio in real time.", "voice_id": "tc_672c5f5ce59fac2a48faeaee", "output": {"audio_format": "wav", "target_lufs": -14.0} }' | ffplay -autoexit -nodisp -loglevel error -i pipe:0 ``` | Parameter | Type | Range | Default | Description | |-----------|------|-------|---------|-------------| | `audio_pitch` | integer | -12–12 | 0 | Pitch adjustment in semitones. | | `audio_tempo` | number | 0.5–2.0 | 1.0 | Speed multiplier. | | `audio_format` | string | wav, mp3 | wav | Output audio format. | | `remove_silence_ms` | integer / null | 0–1000 | null | Silence to retain (ms). 0 removes detected silence; omitted/null disables processing. | | `target_lufs` | number | -70–0 | - | Absolute loudness normalization in LUFS. | Use `target_lufs` to keep streaming audio loudness consistent across clips. `volume` is not supported in streaming mode. ## Generate subtitles with Timestamp TTS Need word-level timing for captions, karaoke, or lip-sync? Use the timestamp TTS endpoint - it returns the audio together with per-word (and optionally per-character) alignment data. ```python Python from typecast import Typecast from typecast.models import TTSRequestWithTimestamps client = Typecast(api_key="YOUR_API_KEY") result = client.text_to_speech_with_timestamps(TTSRequestWithTimestamps( text="Hello. How are you?", model="ssfm-v30", voice_id="tc_60e5426de8b95f1d3000d7b5", )) with open("output.wav", "wb") as f: f.write(result.audio_bytes()) # Export SRT captions with open("output.srt", "w") as f: f.write(result.to_srt()) ``` ```javascript Javascript import { TypecastClient } from '@neosapience/typecast-js'; import fs from 'fs'; const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' }); const result = await client.textToSpeechWithTimestamps({ text: "Hello. How are you?", model: "ssfm-v30", voice_id: "tc_60e5426de8b95f1d3000d7b5", }); await fs.promises.writeFile("output.wav", result.audioBytes()); await fs.promises.writeFile("output.srt", result.toSrt(), "utf-8"); ``` ```bash cURL curl -X POST "https://api.typecast.ai/v1/text-to-speech/with-timestamps" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "ssfm-v30", "text": "Hello. How are you?", "voice_id": "tc_60e5426de8b95f1d3000d7b5" }' ``` See each [SDK documentation](/sdk/python) for all 11 language examples including subtitle export helpers (`toSrt()`, `toVtt()`). ## Next steps Congratulations on creating your first AI voice! Here are some resources to help you dive deeper: Learn how to use the Typecast API Learn about ssfm-v30 and ssfm-v21 models See the latest API changes and updates ## Control silence duration `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. ```json { "output": { "remove_silence_ms": 300 } } ``` See the [SDK overview](/docs/sdk/overview) for minimum supporting versions. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Models ## About our foundation model SSFM Typecast currently uses an advanced AI voice model, the Typecast Speech Synthesis Foundation Model, or Typecast SSFM for short, which is our next generation text-to-speech technology that brings text to life with unparalleled naturalness and expressiveness. ## Models overview | Model | Release Date | Description | | :------- | :----------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ssfm-v30 | 2026.01 | - More natural-sounding speech with smoother prosody and pacing
- Emotion controls with 7 emotion presets
- 37 languages supported
- Smart Emotion available | | ssfm-v21 | 2025.07 | - Low latency
- Emotion controls with 4 emotion presets
- 27 languages supported | ## ssfm-v30 - Smart Emotion: Automatically detects the appropriate emotion from the text context and applies it to the voice. - Emotion Presets: `normal`, `happy`, `sad`, `angry`, `whisper`, `toneup`, `tonedown` (available across all voices) - Languages Supported: English, Korean, Arabic, Bengali, Bulgarian, Cantonese, Chinese (Mandarin), Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hindi, Hungarian, Indonesian, Italian, Japanese, Malay, Min Nan, Norwegian, Polish, Portuguese, Punjabi, Romanian, Russian, Slovak, Spanish, Swedish, Tagalog, Tamil, Thai, Turkish, Ukrainian, Vietnamese ## ssfm-v21 - Emotion Presets: `normal`, `happy`, `sad`, `angry` (availability varies by voice) - Languages Supported: English, Korean, Arabic, Bulgarian, Chinese, Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Indonesian, Italian, Japanese, Malay, Polish, Portuguese, Romanian, Russian, Slovak, Spanish, Swedish, Tagalog, Tamil, Ukrainian --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Changelog All 13 official SDKs, Cast v1.0.10, n8n 1.2.5, Zapier 2.2.7, Pipecat 0.3.1, and the hosted Typecast API MCP now support `remove_silence_ms`. See the [SDK overview](/docs/sdk/overview) for minimum versions. `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. The corresponding TEN and LlamaIndex integration updates remain in upstream PR review and are not included in this released support. ### New parameter: remove\_silence\_ms Added `remove_silence_ms` to shorten detected silence in generated speech. Specify the duration of silence to retain in milliseconds, rather than the amount to remove. * **Input:** An integer from `0` to `1000` ms. `0` removes detected silence; omitting the field or setting it to `null` disables duration-based silence removal. * **TTS, Streaming TTS, and TTS with timestamps:** Set `output.remove_silence_ms`. Returned timestamps align with the audio after silence removal. * **Compose:** Set `segments[].output.remove_silence_ms` on each `tts` segment. Explicit `pause` segments are preserved. See the API Reference for [TTS](/docs/api-reference/text-to-speech/text-to-speech), [Streaming TTS](/docs/api-reference/text-to-speech/streaming-text-to-speech), [TTS with timestamps](/docs/api-reference/text-to-speech/text-to-speech-with-timestamps), and [Compose](/docs/api-reference/text-to-speech/compose-text-to-speech). ### New Endpoint: POST /v1/custom-voices/professional-clone Added asynchronous Professional Voice Cloning. Submit a WAV or MP3 sample with its language, then check the returned custom voice until its status becomes `completed` or `failed`. This release also updates the V3 voices and custom voices APIs, official SDK packages, and Cast CLI. Latest releases include Python 0.3.14, JavaScript 0.4.12, Go 0.3.13, Rust 0.3.13, and Cast CLI v1.0.9 with Professional Voice Cloning support. ### New Endpoint: GET /v1/voices/recommendations Added semantic voice recommendations based on a natural-language description. ### New Endpoint: POST /v1/text-to-speech/compose Added Compose TTS for synthesizing multiple text segments with segment-level voice and speech settings in one request. ### New Endpoint: POST /v1/voices/clone Added Instant Voice Cloning from a WAV or MP3 sample. The original endpoint is now deprecated; use `POST /v1/custom-voices/instant-clone` for new integrations. ### New Endpoint: POST /v1/text-to-speech/with-timestamps Returns the synthesized audio together with word- and character-level alignment data in a single response - ideal for auto-subtitling, karaoke highlights, and lip-sync animations. ```text POST /v1/text-to-speech/with-timestamps ``` **Request Schema:** ```json { "voice_id": "tc_60e5426de8b95f1d3000d7b5", "text": "Hello.", "model": "ssfm-v30" } ``` **Response Schema (summary):** ```json { "audio": "", "audio_format": "wav", "audio_duration": 0.52, "words": [ { "text": "Hello.", "start": 0.0, "end": 0.52 } ], "characters": [ { "text": "H", "start": 0.0, "end": 0.08 }, { "text": "e", "start": 0.08, "end": 0.18 }, { "text": "l", "start": 0.18, "end": 0.28 }, { "text": "l", "start": 0.28, "end": 0.36 }, { "text": "o", "start": 0.36, "end": 0.48 }, { "text": ".", "start": 0.48, "end": 0.52 } ] } ``` **`granularity` parameter:** `granularity` is optional. If omitted, the API returns both word- and character-level alignment in a single response. | Value | Description | | :----- | :-------------------------------------------------------------------------- | | `word` | Per-word alignment. Recommended for all languages with whitespace. | | `char` | Per-character alignment. Required for Japanese (`jpn`) and Chinese (`zho`). | **Captioning rules:** Captions are split on sentence terminators (`. ? ! 。 ? !`) with a 7 s / 42-character hard cap per cue (BBC/Netflix subtitle guidelines). ### SDK Updates - Timestamp TTS added to all 11 SDKs | SDK | Version | Method | | :--------- | :------ | :------------------------------------------ | | Python | 0.3.0 | `text_to_speech_with_timestamps()` | | JavaScript | 0.4.0 | `textToSpeechWithTimestamps()` | | Go | v0.3.0 | `TextToSpeechWithTimestamps()` | | Rust | 0.3.0 | `text_to_speech_with_timestamps()` | | Swift | v0.3.0 | `textToSpeechWithTimestamps()` | | C# | 0.3.0 | `TextToSpeechWithTimestampsAsync()` | | Java | 1.2.0 | `textToSpeechWithTimestamps()` | | Kotlin | 1.2.0 | `textToSpeechWithTimestamps()` | | C | 1.2.0 | `typecast_text_to_speech_with_timestamps()` | | Zig | v0.2.0 | `textToSpeechWithTimestamps()` | | PHP | v0.1.0 | `textToSpeechWithTimestamps()` | All SDK response objects include `toSrt()` / `toVtt()` subtitle export helpers and a `saveAudio(path)` / `audio_bytes()` convenience method. ### New Endpoint: POST /v1/text-to-speech/stream Added a low-latency streaming endpoint that delivers audio chunks as they are generated, enabling real-time playback without waiting for full synthesis. ```text POST /v1/text-to-speech/stream ``` **Key Differences from `/v1/text-to-speech`:** | Feature | Standard | Streaming | | :-------------- | :---------------------- | :-------------------------------------------------- | | Response | Complete audio file | Chunked audio stream | | Latency | Wait for full synthesis | First chunk in \~200ms | | `volume` | Supported | Not supported | | `target_lufs` | Supported | Supported | | Output settings | `Output` | `OutputStream` (pitch, tempo, format, target\_lufs) | **Request Schema:** ```json { "voice_id": "tc_xxxxx", "text": "Thanks for reaching out. Your reservation has been confirmed for Friday at 7 PM.", "model": "ssfm-v30", "language": "eng", "output": { "audio_pitch": 0, "audio_tempo": 1.0, "audio_format": "wav" } } ``` **Response:** Chunked binary stream (`audio/wav` or `audio/mpeg`). ### New Endpoint: GET /v1/users/me/subscription Retrieve the authenticated user's plan tier, credit usage, and concurrency limits. ```text GET /v1/users/me/subscription ``` **Response Schema:** ```json { "plan": "lite", "credits": { "plan_credits": 200000, "used_credits": 157300 }, "limits": { "concurrency_limit": 5 } } ``` ### SDK Updates All 9 official SDKs have been updated with streaming and subscription support: | SDK | Version | Streaming Method | | :--------- | :------ | :--------------------------------------------- | | Python | 0.2.0 | `text_to_speech_stream()` (sync + async) | | JavaScript | 0.3.0 | `textToSpeechStream()` → `ReadableStream` | | Go | v0.2.0 | `TextToSpeechStream()` → `io.ReadCloser` | | Rust | 0.2.0 | `text_to_speech_stream()` → `Stream` | | Swift | v0.2.0 | `textToSpeechStream()` → `AsyncThrowingStream` | | C# | 0.2.0 | `TextToSpeechStreamAsync()` → `Stream` | | Java | 1.1.0 | `textToSpeechStream()` → `InputStream` | | Kotlin | 1.1.0 | `textToSpeechStream()` → `InputStream` | | C | 1.1.0 | `typecast_text_to_speech_stream()` (callback) | ### New Model: ssfm-v30 Added support for the new `ssfm-v30` model with improved speech quality and expanded capabilities. **New Features:** * **Smart Emotion** - Context-aware emotion inference using `SmartPrompt` * **7 Emotion Presets** - Added `whisper`, `toneup`, `tonedown` presets * **Universal Emotion Support** - All emotions available across all voices * **37 Languages** - Added 10 new languages **New Languages:** Bengali, Cantonese, Hindi, Hungarian, Min Nan, Norwegian, Punjabi, Thai, Turkish, Vietnamese **Request Schema Changes:** ```json // ssfm-v30 with SmartPrompt (context-aware emotion) { "model": "ssfm-v30", "prompt": { "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!", "next_text": "I am literally bursting with happiness and I never want this feeling to end!" } } // ssfm-v30 with PresetPrompt (manual emotion selection) { "model": "ssfm-v30", "prompt": { "emotion_type": "preset", "emotion_preset": "happy", "emotion_intensity": 1.0 } } ``` ### New Endpoint: GET /v2/voices Added enhanced voice listing endpoint with model-grouped emotions and additional metadata. ```text GET /v2/voices ``` **Query Parameters:** | Parameter | Type | Description | | :---------- | :----- | :------------------------------------------------------------------------------ | | `model` | string | Filter by model (`ssfm-v21`, `ssfm-v30`) | | `gender` | string | Filter by gender (`male`, `female`) | | `age` | string | Filter by age group (`child`, `teenager`, `young_adult`, `middle_age`, `elder`) | | `use_cases` | string | Filter by use case (`Audiobook`, `Game`, `E-learning`, etc.) | **Response Schema:** ```json [ { "voice_id": "tc_xxxxx", "voice_name": "Voice Name", "models": [ { "version": "ssfm-v30", "emotions": ["normal", "happy", "sad", "angry", "whisper", "toneup", "tonedown"] }, { "version": "ssfm-v21", "emotions": ["normal", "happy", "sad"] } ], "gender": "female", "age": "young_adult", "use_cases": ["Audiobook", "E-learning"] } ] ``` ### Deprecated: Voice Management Endpoints The following endpoints have been deprecated and removed: | Endpoint | Status | | :-------------------------- | :------ | | `POST /v1/voices` | Removed | | `GET /v1/voices/{voice_id}` | Removed | Use `GET /v2/voices` for listing voices with enhanced metadata. ### Initial Release: ssfm-v21 Launched the Typecast Text-to-Speech API with the `ssfm-v21` model. **Endpoints:** | Method | Endpoint | Description | | :----- | :------------------- | :------------------------ | | POST | `/v1/text-to-speech` | Generate speech from text | | GET | `/v1/voices` | List available voices | **Features:** * Low latency speech synthesis * 4 Emotion presets: `normal`, `happy`, `sad`, `angry` * Emotion availability varies by voice * 27 languages supported **Supported Languages:** English, Korean, Arabic, Bulgarian, Chinese, Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Indonesian, Italian, Japanese, Malay, Polish, Portuguese, Romanian, Russian, Slovak, Spanish, Swedish, Tagalog, Tamil, Ukrainian **Request Schema:** ```json { "voice_id": "tc_xxxxx", "text": "Everything is so incredibly perfect that I feel like I'm dreaming.", "model": "ssfm-v21", "language": "eng", "prompt": { "emotion_preset": "normal", "emotion_intensity": 1.0 }, "output": { "volume": 100, "audio_pitch": 0, "audio_tempo": 1.0, "audio_format": "wav" } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # SDK Overview ## Why use an SDK? You can call the Typecast API directly over HTTP, but SDKs are recommended for production code. SDKs wrap repetitive integration work such as authentication headers, request and response types, error handling, streaming handling, and response parsing, so you can integrate more safely and faster than implementing every HTTP call yourself. They also provide higher-level workflows such as **voice recommendations**, **text pauses**, and **multi-speaker composition**, which are cumbersome to implement with direct API calls alone. Use the SDK that matches where your Typecast API integration will run. If you are starting from a script or prototype, Python is usually the fastest path. For production services, choose the SDK that fits your existing backend or app runtime. | SDK | Use when | | --- | --- | | [Python](/sdk/python) | You are building scripts, notebooks, data pipelines, backend jobs, or quick API prototypes. | | [Javascript/Typescript](/sdk/javascript) | You are building Node.js services, frontend tooling, full-stack apps, or browser-compatible integrations. | | [Go](/sdk/go) | You need a lightweight backend service, CLI, worker, or concurrent batch process. | | [Rust](/sdk/rust) | You need strong type safety, predictable performance, or a native audio processing pipeline. | | [C#/.NET](/sdk/csharp) | You are building .NET services, Windows tools, Unity apps, or Blazor applications. | | [Java](/sdk/java) | You are integrating with JVM backend systems, Spring services, or Java-first enterprise codebases. | | [Kotlin](/sdk/kotlin) | You are building Kotlin-first JVM services or Android applications. | | [C/C++](/sdk/c) | You need native integration, embedded support, FFI bindings, or minimal runtime overhead. | | [Swift](/sdk/swift) | You are building iOS, macOS, watchOS, tvOS, or visionOS applications. | | [Zig](/sdk/zig) | You want a low-level native integration with explicit memory control and no C dependency. | | [PHP](/sdk/php) | You are integrating Typecast into Laravel, WordPress, or PHP server-rendered backends. | | [Dart/Flutter](/sdk/dart) | You are building Flutter mobile, desktop, or web applications. | | [Ruby](/sdk/ruby) | You are building Rails apps, Ruby backend jobs, or internal automation scripts. | ## Utility SDK | SDK | Use when | | --- | --- | | [Autotag SDK](/bestpractice/autotag) | You need to preprocess phone numbers, dates, times, amounts, and other structured text so TTS reads it more naturally. | ## Control silence duration `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. | SDK | Minimum supporting version | | --- | --- | | [python](/docs/sdk/python) | 0.3.15 | | [javascript](/docs/sdk/javascript) | 0.4.13 | | [go](/docs/sdk/go) | 0.3.14 | | [rust](/docs/sdk/rust) | 0.3.15 | | [csharp](/docs/sdk/csharp) | 0.3.13 | | [java](/docs/sdk/java) | 1.2.12 | | [kotlin](/docs/sdk/kotlin) | 1.2.13 | | [c](/docs/sdk/c) | 1.2.13 | | [swift](/docs/sdk/swift) | 0.3.14 | | [zig](/docs/sdk/zig) | 0.2.12 | | [php](/docs/sdk/php) | 0.1.14 | | [dart](/docs/sdk/dart) | 0.1.13 | | [ruby](/docs/sdk/ruby) | 0.1.11 | --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Python Typecast Python SDK Typecast Python SDK Source Code Python 3.10 through 3.14 is supported. **Python 3.8 and 3.9 are no longer supported because they have reached end of life (EOL).** The last SDK supporting these versions is **typecast-python 0.3.15**. Upgrade Python to 3.10 or later (below 3.15), then install the current SDK. If you must temporarily keep an older environment, pin `python -m pip install "typecast-python==0.3.15"`; this does not restore security support for the EOL runtime. ## Installation Install the Typecast Python SDK using pip: ```bash pip install --upgrade typecast-python ``` The package is installed as `typecast-python`, but imported as `typecast`. Latest registered version: **0.4.0** on PyPI. Make sure you have **version 0.4.0 or higher** installed. You can check your version with `pip show typecast-python`. If you have an older version, run `pip install --upgrade typecast-python` to update. ## Quick Start Here's a simple example to convert text to speech: ```python from typecast import Typecast from typecast.models import TTSRequest # Initialize client client = Typecast(api_key="YOUR_API_KEY") # Convert text to speech response = client.text_to_speech(TTSRequest( text="Hello there! I'm your friendly text-to-speech agent.", model="ssfm-v30", voice_id="tc_672c5f5ce59fac2a48faeaee" )) # Save audio file with open('output.wav', 'wb') as f: f.write(response.audio_data) print(f"Duration: {response.duration}s, Format: {response.format}") ``` ## Features The Typecast Python SDK provides powerful features for text-to-speech conversion: - **Multiple Voice Models**: Support for `ssfm-v30` (latest) and `ssfm-v21` AI voice models - **Multi-language Support**: 35+ languages including English, Korean, Spanish, Japanese, Chinese, and more - **Emotion Control**: Preset emotions (normal, happy, sad, angry, whisper, toneup, tonedown) or smart context-aware inference - **Audio Customization**: Control loudness (LUFS -70 to 0), pitch (-12 to +12 semitones), tempo (0.5x to 2.0x), and format (WAV/MP3) - **Async Support**: Built-in async client for high-performance applications - **Voice Discovery**: V2 Voices API with filtering by model, gender, age, and use cases - **Instant Voice Cloning**: Upload a WAV/MP3 sample and create a custom voice ID - **Type Hints**: Full type annotations with Pydantic models - **Timestamp TTS**: Word- and character-level alignment data for subtitles, karaoke, and lip-sync - **Streaming**: Real-time chunked audio delivery for low-latency playback ## Voice Recommendations Use `recommend_voices` when you know the desired style but not the exact `voice_id`. ```python recommendations = client.recommend_voices( "warm female voice for a product tutorial", count=3, ) for voice in recommendations: print(voice.voice_id, voice.voice_name, voice.score) ``` Recommendation results contain only `voice_id`, `voice_name`, and `score`. Use `voice_v2(voice_id)` or `voices_v2()` when you need detailed metadata such as supported models, emotions, gender, age, or use cases. ## Configuration You can configure the API key using environment variables or pass it directly to the client: ```bash Environment Variable export TYPECAST_API_KEY="your-api-key-here" ``` ```python From Environment from typecast import Typecast # From environment variable client = Typecast() ``` ```python Direct Configuration from typecast import Typecast # Or pass directly client = Typecast(api_key="your-api-key-here") ``` When requests go through your own proxy, set `TYPECAST_API_HOST` or pass `api_host` and omit `api_key`. The SDK will not send the `X-API-KEY` header for empty or missing keys. Requests to the default Typecast host still require an API key. ```python Proxy without API key from typecast import Typecast client = Typecast(api_host="https://your-proxy.example.com") ``` ## Advanced Usage ### Emotion Control (ssfm-v30) ssfm-v30 offers two emotion control modes: **Preset** and **Smart**. Let the AI infer emotion from context: ```python from typecast import Typecast from typecast.models import TTSRequest, SmartPrompt client = Typecast() response = client.text_to_speech(TTSRequest( text="Everything is going to be okay.", model="ssfm-v30", voice_id="tc_672c5f5ce59fac2a48faeaee", prompt=SmartPrompt( emotion_type="smart", previous_text="I just got the best news!", # Optional context next_text="I can't wait to celebrate!" # Optional context ) )) ``` Explicitly set emotion with preset values: ```python from typecast import Typecast from typecast.models import TTSRequest, PresetPrompt client = Typecast() response = client.text_to_speech(TTSRequest( text="I am so excited to show you these features!", model="ssfm-v30", voice_id="tc_672c5f5ce59fac2a48faeaee", prompt=PresetPrompt( emotion_type="preset", emotion_preset="happy", # normal, happy, sad, angry, whisper, toneup, tonedown emotion_intensity=1.5 # Range: 0.0 to 2.0 ) )) ``` ### Audio Customization Control loudness, pitch, tempo, and output format: ```python from typecast import Typecast from typecast.models import TTSRequest, Output client = Typecast() response = client.text_to_speech(TTSRequest( text="Customized audio output!", model="ssfm-v30", voice_id="tc_672c5f5ce59fac2a48faeaee", output=Output( target_lufs=-14.0, # Range: -70 to 0 (LUFS) audio_pitch=2, # Range: -12 to +12 semitones audio_tempo=1.2, # Range: 0.5x to 2.0x audio_format="mp3" # Options: wav, mp3 ), seed=42 # Unsigned seed for reproducible results )) ``` ### Generate audio to a file Use `generate_to_file` when you want the SDK to synthesize speech and write the audio bytes directly to a local file. The model defaults to `ssfm-v30`, and `.mp3` / `.wav` extensions infer the output format when no output format is set. Browse available voice IDs on the [Voices](https://studio.typecast.ai/developers/api/voices) page. ```python client.generate_to_file( 'output.mp3', text='Hello from Typecast.', voice_id='tc_672c5f5ce59fac2a48faeaee' # Find voice IDs at https://studio.typecast.ai/developers/api/voices ) # Async client await async_client.generate_to_file( 'output.mp3', text='Hello from Typecast.', voice_id='tc_672c5f5ce59fac2a48faeaee' # Find voice IDs at https://studio.typecast.ai/developers/api/voices ) ``` ### Text pauses Use text pause markup when you only need silent gaps inside one composed text segment. Put `<|5s|>`, `<|1s|>`, `<|0.3s|>`, or `<|0.34413s|>` directly in the text. The value is interpreted as seconds and must end with `s`. This keeps the pause expression visible in plain text without adding separate pause calls. ```python response = ( client.compose_speech() .defaults(voice_id="tc_672c5f5ce59fac2a48faeaee", model="ssfm-v30") .say("Hello<|5s|>Nice to meet you<|1s|>Today<|2s|>how does the weather feel?") .generate() ) ``` ### Multi-speaker composition Use the composer chaining API when one output file needs different voices or per-segment options such as pitch, tempo, prompt, or seed. The composer sends segments to `POST /v1/text-to-speech/compose`, which returns WAV or MP3 directly. Set silence removal explicitly on TTS segments; explicit pauses are preserved. ```python from typecast import Typecast from typecast.models import Output client = Typecast(api_key="YOUR_API_KEY") response = ( client.compose_speech() .defaults(voice_id="tc_672c5f5ce59fac2a48faeaee", model="ssfm-v30") .say("Hello there") .pause(5) .say("Nice to meet you", voice_id="tc_60e5426de8b95f1d3000d7b5", output=Output(audio_pitch=2)) .say("Today") .pause(2) .say("How does the weather feel?") .generate() ) with open("conversation.wav", "wb") as f: f.write(response.audio_data) ``` ### Voice Discovery (V2 API) List and filter available voices with enhanced metadata: ```python from typecast import Typecast from typecast.models import VoicesV2Filter, TTSModel, GenderEnum, AgeEnum client = Typecast() # Get all voices voices = client.voices_v2() # Filter by criteria filtered = client.voices_v2(VoicesV2Filter( model=TTSModel.SSFM_V30, gender=GenderEnum.FEMALE, age=AgeEnum.YOUNG_ADULT )) # Display voice info for voice in voices: print(f"ID: {voice.voice_id}, Name: {voice.voice_name}") print(f"Gender: {voice.gender}, Age: {voice.age}") print(f"Models: {', '.join(m.version.value for m in voice.models)}") print(f"Use cases: {voice.use_cases}") ``` ### Async Client For high-performance applications, use the async client: ```python import asyncio from typecast import AsyncTypecast from typecast.models import TTSRequest async def main(): async with AsyncTypecast() as client: response = await client.text_to_speech(TTSRequest( text="Hello from async!", model="ssfm-v30", voice_id="tc_672c5f5ce59fac2a48faeaee" )) with open('async_output.wav', 'wb') as f: f.write(response.audio_data) asyncio.run(main()) ``` ### Streaming Stream audio chunks in real-time for low-latency playback: ```python # pip install requests sounddevice import sounddevice as sd from typecast import Typecast from typecast.models import TTSRequestStream, OutputStream client = Typecast() request = TTSRequestStream( text="Stream this text as audio in real time.", model="ssfm-v30", voice_id="tc_672c5f5ce59fac2a48faeaee", output=OutputStream(audio_format="wav") ) with sd.RawOutputStream(samplerate=32000, channels=1, dtype="int16") as player: buf, first = bytearray(), True for chunk in client.text_to_speech_stream(request): if first: chunk = chunk[44:] # Skip 44-byte WAV header first = False buf.extend(chunk) n = len(buf) - (len(buf) % 2) # int16 alignment if n: player.write(bytes(buf[:n])) del buf[:n] ``` **WAV streaming format:** 32000 Hz, 16-bit, mono PCM. The first chunk includes a 44-byte WAV header (size = `0xFFFFFFFF`); subsequent chunks are raw PCM only. For MP3: 320 kbps, 44100 Hz, each chunk is independently decodable. ## Timestamp TTS `text_to_speech_with_timestamps()` wraps `POST /v1/text-to-speech/with-timestamps` and returns the audio together with per-word and per-character alignment data - useful for karaoke highlights, subtitle generation, and lip-sync applications. ### Basic Usage ```python from typecast import Typecast from typecast.models import TTSRequestWithTimestamps client = Typecast(api_key="YOUR_API_KEY") response = client.text_to_speech_with_timestamps(TTSRequestWithTimestamps( text="Hello. How are you?", model="ssfm-v30", voice_id="tc_60e5426de8b95f1d3000d7b5", )) # Save audio with open("output.wav", "wb") as f: f.write(response.audio_bytes()) print(f"Duration: {response.audio_duration}s") for word in response.words: print(f" [{word.start_time:.3f}s – {word.end_time:.3f}s] {word.text}") ``` ### Granularity Pass `granularity="word"` (default) or `granularity="char"` to control the alignment unit. ```python # Character-level alignment - required for Japanese / Chinese response = client.text_to_speech_with_timestamps(TTSRequestWithTimestamps( text="Hello. How are you?", model="ssfm-v30", voice_id="tc_60e5426de8b95f1d3000d7b5", granularity="char", )) for char in response.characters: print(f" [{char.start_time:.3f}s – {char.end_time:.3f}s] {char.text}") ``` ### Subtitle Export The response object includes helpers that convert alignment data to SRT or WebVTT captions. Captions are split on sentence terminators (`. ? ! 。 ? !`) and capped at 7 seconds / 42 characters per cue (BBC/Netflix subtitle guidelines). ```python # Export SRT captions srt_text = response.to_srt() with open("output.srt", "w", encoding="utf-8") as f: f.write(srt_text) # Export WebVTT captions vtt_text = response.to_vtt() with open("output.vtt", "w", encoding="utf-8") as f: f.write(vtt_text) ``` ### Save Audio Helper ```python # Equivalent to writing audio_bytes() to a file response.save_audio("output.wav") ``` **Japanese / Chinese:** Word-level segmentation is not meaningful for languages without whitespace delimiters (jpn, zho). Use `granularity="char"` for these languages to get character-level alignment. ## Instant Voice Cloning Clone a custom voice from a short audio sample, then pass the returned `uc_` voice ID directly to TTS. ```python from typecast import Typecast from typecast.models import TTSRequest client = Typecast(api_key="YOUR_API_KEY") voice = client.clone_voice( audio="sample.wav", name="my-voice", model="ssfm-v30", ) response = client.text_to_speech(TTSRequest( text="Hello from my cloned voice!", voice_id=voice.voice_id, model="ssfm-v30", )) with open("output.wav", "wb") as f: f.write(response.audio_data) client.delete_voice(voice.voice_id) ``` Voice cloning audio must be **25 MB or smaller**, the audio duration must be **5-150 seconds**, and the custom voice name must be **1-30 characters**. ## Supported Languages **Recommended**: Use the `LanguageCode` enum for type-safe language selection. You can also pass the ISO 639-3 code as a string (e.g., `"eng"`). The SDK supports 35+ languages with ISO 639-3 codes: | Language | Code | Language | Code | Language | Code | |----------|------|----------|------|----------|------| | English | `eng` | Japanese | `jpn` | Ukrainian | `ukr` | | Korean | `kor` | Greek | `ell` | Indonesian | `ind` | | Spanish | `spa` | Tamil | `tam` | Danish | `dan` | | German | `deu` | Tagalog | `tgl` | Swedish | `swe` | | French | `fra` | Finnish | `fin` | Malay | `msa` | | Italian | `ita` | Chinese | `zho` | Czech | `ces` | | Polish | `pol` | Slovak | `slk` | Portuguese | `por` | | Dutch | `nld` | Arabic | `ara` | Bulgarian | `bul` | | Russian | `rus` | Croatian | `hrv` | Romanian | `ron` | | Bengali | `ben` | Hindi | `hin` | Hungarian | `hun` | | Hokkien | `nan` | Norwegian | `nor` | Punjabi | `pan` | | Thai | `tha` | Turkish | `tur` | Vietnamese | `vie` | | Cantonese | `yue` | | | | | Use the `LanguageCode` enum for type-safe language selection: ```python from typecast.models import TTSRequest, LanguageCode response = client.text_to_speech(TTSRequest( text="Hello", model="ssfm-v30", voice_id="tc_672c5f5ce59fac2a48faeaee", language=LanguageCode.ENG )) ``` ## Error Handling The SDK provides specific exceptions for different HTTP status codes: ```python from typecast import ( Typecast, TypecastError, BadRequestError, UnauthorizedError, PaymentRequiredError, NotFoundError, UnprocessableEntityError, RateLimitError, InternalServerError, ) try: response = client.text_to_speech(request) except UnauthorizedError: print("Invalid API key") except PaymentRequiredError: print("Insufficient credits") except RateLimitError: print("Rate limit exceeded - please retry later") except TypecastError as e: print(f"Error {e.status_code}: {e.message}") ``` | Exception | Status Code | Description | |-----------|-------------|-------------| | `BadRequestError` | 400 | Invalid request parameters | | `UnauthorizedError` | 401 | Invalid or missing API key | | `PaymentRequiredError` | 402 | Insufficient credits | | `NotFoundError` | 404 | Resource not found | | `UnprocessableEntityError` | 422 | Validation error | | `RateLimitError` | 429 | Rate limit exceeded | | `InternalServerError` | 500 | Server error | ## Control silence duration Requires **0.3.15 or later**. `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. Pass these output settings to the corresponding request's `output`. For streaming, use the streaming output type. ```python from typecast.models import Output, OutputStream output = Output(remove_silence_ms=300) stream_output = OutputStream(remove_silence_ms=300) ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Javascript/Typescript The official Node.js library for the [Typecast API](https://typecast.ai). Convert text to lifelike speech using AI-powered voices. Works with both Javascript and TypeScript. Full TypeScript types included. ESM & CommonJS supported. Works in Node.js 18+ and modern browsers. Node.js 16/17 users need to install `isomorphic-fetch` polyfill. Typecast Javascript/Typescript SDK Typecast Javascript/Typescript SDK Source Code ## Installation ```bash npm install @neosapience/typecast-js@latest ``` ```bash pnpm add @neosapience/typecast-js@latest ``` ```bash yarn add @neosapience/typecast-js@latest ``` Latest registered version: **0.4.13** on npm. Make sure you have **version 0.4.13 or higher** installed. You can check your version with `npm list @neosapience/typecast-js`. If you have an older version, run `npm update @neosapience/typecast-js` to update. ## Quick Start ```typescript import { TypecastClient } from '@neosapience/typecast-js'; import fs from 'fs'; async function main() { const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' }); const audio = await client.textToSpeech({ text: "Hello there! I'm your friendly text-to-speech agent.", model: "ssfm-v30", voice_id: "tc_672c5f5ce59fac2a48faeaee" }); await fs.promises.writeFile(`output.${audio.format}`, Buffer.from(audio.audioData)); console.log(`Audio saved! Duration: ${audio.duration}s, Format: ${audio.format}`); } main(); ``` ```javascript const { TypecastClient } = require('@neosapience/typecast-js'); const fs = require('fs'); async function main() { const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' }); const audio = await client.textToSpeech({ text: "Hello there! I'm your friendly text-to-speech agent.", model: "ssfm-v30", voice_id: "tc_672c5f5ce59fac2a48faeaee" }); await fs.promises.writeFile(`output.${audio.format}`, Buffer.from(audio.audioData)); console.log(`Audio saved! Duration: ${audio.duration}s, Format: ${audio.format}`); } main(); ``` ## Features The Typecast Javascript/TypeScript SDK provides powerful features for text-to-speech conversion: - **Multiple Voice Models**: Support for `ssfm-v30` (latest) and `ssfm-v21` AI voice models - **Multi-language Support**: 35+ languages including English, Korean, Spanish, Japanese, Chinese, and more - **Emotion Control**: Preset emotions (normal, happy, sad, angry, whisper, toneup, tonedown) or smart context-aware inference - **Audio Customization**: Control loudness (LUFS -70 to 0), pitch (-12 to +12 semitones), tempo (0.5x to 2.0x), and format (WAV/MP3) - **Voice Discovery**: V2 Voices API with filtering by model, gender, age, and use cases - **Instant Voice Cloning**: Upload a WAV/MP3 sample and create a custom voice ID - **TypeScript Support**: Full type definitions included - **Timestamp TTS**: Word- and character-level alignment data for subtitles, karaoke, and lip-sync - **Zero Dependencies**: Uses native fetch API (works in Node.js 18+ and browsers) - **Streaming**: Real-time chunked audio delivery for low-latency playback ## Voice Recommendations Use `recommendVoices` when you know the desired style but not the exact `voice_id`. ```typescript const recommendations = await client.recommendVoices( 'warm female voice for a product tutorial', 3 ); for (const voice of recommendations) { console.log(voice.voice_id, voice.voice_name, voice.score); } ``` Recommendation results contain only `voice_id`, `voice_name`, and `score`. Use `getVoiceV2(voiceId)` or `getVoicesV2()` when you need detailed metadata such as supported models, emotions, gender, age, or use cases. ## Configuration Set your API key via environment variable or constructor: ```typescript // Using environment variable // export TYPECAST_API_KEY="your-api-key-here" const client = new TypecastClient({ apiKey: process.env.TYPECAST_API_KEY! }); // Or pass directly const client = new TypecastClient({ apiKey: 'your-api-key-here' }); ``` When requests go through your own proxy, set `baseHost` to the proxy endpoint and omit `apiKey`. The SDK will not send the `X-API-KEY` header for empty or missing keys. ```typescript Proxy without API key const client = new TypecastClient({ baseHost: 'https://your-proxy.example.com' }); ``` ## Advanced Usage ### Emotion Control (ssfm-v30) ssfm-v30 offers two emotion control modes: **Preset** and **Smart**. Let the AI infer emotion from context: ```typescript import { SmartPrompt } from '@neosapience/typecast-js'; const audio = await client.textToSpeech({ text: "Everything is going to be okay.", voice_id: "tc_672c5f5ce59fac2a48faeaee", model: "ssfm-v30", prompt: { emotion_type: "smart", previous_text: "I just got the best news!", // Optional context next_text: "I can't wait to celebrate!" // Optional context } as SmartPrompt }); ``` Explicitly set emotion with preset values: ```typescript import { PresetPrompt } from '@neosapience/typecast-js'; const audio = await client.textToSpeech({ text: "I am so excited to show you these features!", voice_id: "tc_672c5f5ce59fac2a48faeaee", model: "ssfm-v30", prompt: { emotion_type: "preset", emotion_preset: "happy", // normal, happy, sad, angry, whisper, toneup, tonedown emotion_intensity: 1.5 // Range: 0.0 to 2.0 } as PresetPrompt }); ``` ### Audio Customization Control loudness, pitch, tempo, and output format: ```javascript const response = await client.textToSpeech({ text: "Customized audio output!", model: "ssfm-v30", voice_id: "tc_672c5f5ce59fac2a48faeaee", output: { target_lufs: -14.0, // Range: -70 to 0 (LUFS) audio_pitch: 2, // Range: -12 to +12 semitones audio_tempo: 1.2, // Range: 0.5x to 2.0x audio_format: "mp3" // Options: wav, mp3 } }); ``` ### Generate audio to a file Use `generateToFile` when you want the SDK to synthesize speech and write the audio bytes directly to a local file. The model defaults to `ssfm-v30`, and `.mp3` / `.wav` extensions infer the output format when `output.audio_format` is not set. Browse available voice IDs on the [Voices](https://studio.typecast.ai/developers/api/voices) page. ```typescript await client.generateToFile('output.mp3', { text: 'Hello from Typecast.', voice_id: 'tc_672c5f5ce59fac2a48faeaee' // Find voice IDs at https://studio.typecast.ai/developers/api/voices }); ``` ### Text pauses Use text pause markup when you only need silent gaps inside one composed text segment. Put `<|5s|>`, `<|1s|>`, `<|0.3s|>`, or `<|0.34413s|>` directly in the text. The value is interpreted as seconds and must end with `s`. This keeps the pause expression visible in plain text without adding separate pause calls. ```typescript const audio = await client .composeSpeech() .defaults({ voice_id: 'tc_672c5f5ce59fac2a48faeaee', model: 'ssfm-v30' }) .say('Hello<|5s|>Nice to meet you<|1s|>Today<|2s|>how does the weather feel?') .generate(); ``` ### Multi-speaker composition Use the composer chaining API when one output file needs different voices or per-segment options such as pitch, tempo, prompt, or seed. The composer sends segments to `POST /v1/text-to-speech/compose`, which returns WAV or MP3 directly. Set silence removal explicitly on TTS segments; explicit pauses are preserved. ```typescript import { TypecastClient } from '@neosapience/typecast-js'; import fs from 'fs/promises'; const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' }); const audio = await client .composeSpeech() .defaults({ voice_id: 'tc_672c5f5ce59fac2a48faeaee', model: 'ssfm-v30' }) .say('Hello there') .pause(5) .say('Nice to meet you', { voice_id: 'tc_60e5426de8b95f1d3000d7b5', output: { audio_pitch: 2 } }) .say('Today') .pause(2) .say('How does the weather feel?') .generate(); await fs.writeFile('conversation.wav', Buffer.from(audio.audioData)); ``` ### Voice Discovery (V2 API) List and filter available voices with enhanced metadata: ```typescript // Get all voices const voices = await client.getVoicesV2(); // Filter by criteria const filtered = await client.getVoicesV2({ model: 'ssfm-v30', gender: 'female', age: 'young_adult' }); // Display voice info voices.forEach(voice => { console.log(`ID: ${voice.voice_id}, Name: ${voice.voice_name}`); console.log(`Gender: ${voice.gender}, Age: ${voice.age}`); console.log(`Models: ${voice.models.map(m => m.version).join(', ')}`); console.log(`Use cases: ${voice.use_cases?.join(', ')}`); }); ``` ### Multilingual Content The SDK supports 35+ languages with automatic language detection: ```typescript // Auto-detect language (recommended) const audio = await client.textToSpeech({ text: "こんにちは。お元気ですか。", voice_id: "tc_672c5f5ce59fac2a48faeaee", model: "ssfm-v30" }); // Or specify language explicitly const koreanAudio = await client.textToSpeech({ text: "안녕하세요. 반갑습니다.", voice_id: "tc_672c5f5ce59fac2a48faeaee", model: "ssfm-v30", language: "kor" // ISO 639-3 language code }); await fs.promises.writeFile(`output.${audio.format}`, Buffer.from(audio.audioData)); ``` ### Streaming Stream audio chunks in real-time for low-latency playback: ```javascript // Node 18+ (built-in fetch). Pipe stream to ffplay for real-time playback. // Prerequisite: ffmpeg (brew/choco/apt install ffmpeg) import { spawn } from "node:child_process"; import { TypecastClient } from '@neosapience/typecast-js'; const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' }); const ffplay = spawn( "ffplay", ["-autoexit", "-nodisp", "-loglevel", "error", "-i", "pipe:0"], { stdio: ["pipe", "ignore", "ignore"] }, ); const stream = await client.textToSpeechStream({ text: "Stream this text as audio in real time.", model: "ssfm-v30", voice_id: "tc_672c5f5ce59fac2a48faeaee", output: { audio_format: "wav" } }); // ReadableStream - read chunks as they arrive const reader = stream.getReader(); while (true) { const { value, done } = await reader.read(); if (done) break; ffplay.stdin.write(value); } ffplay.stdin.end(); await new Promise((resolve) => ffplay.on("close", resolve)); ``` **WAV streaming format:** 32000 Hz, 16-bit, mono PCM. The first chunk includes a 44-byte WAV header (size = `0xFFFFFFFF`); subsequent chunks are raw PCM only. For MP3: 320 kbps, 44100 Hz, each chunk is independently decodable. ## Timestamp TTS `textToSpeechWithTimestamps()` wraps `POST /v1/text-to-speech/with-timestamps` and returns the audio together with per-word and per-character alignment data - useful for karaoke highlights, subtitle generation, and lip-sync applications. ### Basic Usage ```typescript import { TypecastClient } from '@neosapience/typecast-js'; import fs from 'fs'; const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' }); const result = await client.textToSpeechWithTimestamps({ text: "Hello. How are you?", model: "ssfm-v30", voice_id: "tc_60e5426de8b95f1d3000d7b5", }); // Save audio await fs.promises.writeFile("output.wav", result.audioBytes()); console.log(`Duration: ${result.audio_duration}s`); result.words.forEach(w => { console.log(` [${w.start_time.toFixed(3)}s – ${w.end_time.toFixed(3)}s] ${w.text}`); }); ``` ### Granularity Pass `granularity: "word"` (default) or `granularity: "char"` to control the alignment unit. ```typescript // Character-level alignment - required for Japanese / Chinese const result = await client.textToSpeechWithTimestamps({ text: "Hello. How are you?", model: "ssfm-v30", voice_id: "tc_60e5426de8b95f1d3000d7b5", granularity: "char", }); result.characters.forEach(c => { console.log(` [${c.start_time.toFixed(3)}s – ${c.end_time.toFixed(3)}s] ${c.text}`); }); ``` ### Subtitle Export The response object includes helpers that convert alignment data to SRT or WebVTT captions. Captions are split on sentence terminators (`. ? ! 。 ? !`) and capped at 7 seconds / 42 characters per cue (BBC/Netflix subtitle guidelines). ```typescript // Export SRT captions const srt = result.toSrt(); await fs.promises.writeFile("output.srt", srt, "utf-8"); // Export WebVTT captions const vtt = result.toVtt(); await fs.promises.writeFile("output.vtt", vtt, "utf-8"); ``` ### Save Audio Helper ```typescript await result.saveAudio("output.wav"); ``` **Japanese / Chinese:** Word-level segmentation is not meaningful for languages without whitespace delimiters (jpn, zho). Use `granularity: "char"` for these languages to get character-level alignment. ## Instant Voice Cloning Clone a custom voice from a short audio sample, then pass the returned `uc_` voice ID directly to TTS. ```typescript import { TypecastClient } from '@neosapience/typecast-js'; import fs from 'node:fs/promises'; const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' }); const voice = await client.cloneVoice({ audio: './sample.wav', name: 'my-voice', model: 'ssfm-v30', }); const audio = await client.textToSpeech({ text: 'Hello from my cloned voice!', voice_id: voice.voiceId, model: 'ssfm-v30', }); await fs.writeFile('output.wav', new Uint8Array(audio.audioData)); await client.deleteVoice(voice.voiceId); ``` Voice cloning audio must be **25 MB or smaller**, the audio duration must be **5-150 seconds**, and the custom voice name must be **1-30 characters**. ## Supported Languages The SDK supports 35+ languages with automatic language detection: | Code | Language | Code | Language | Code | Language | |------|----------|------|----------|------|----------| | `eng` | English | `jpn` | Japanese | `ukr` | Ukrainian | | `kor` | Korean | `ell` | Greek | `ind` | Indonesian | | `spa` | Spanish | `tam` | Tamil | `dan` | Danish | | `deu` | German | `tgl` | Tagalog | `swe` | Swedish | | `fra` | French | `fin` | Finnish | `msa` | Malay | | `ita` | Italian | `zho` | Chinese | `ces` | Czech | | `pol` | Polish | `slk` | Slovak | `por` | Portuguese | | `nld` | Dutch | `ara` | Arabic | `bul` | Bulgarian | | `rus` | Russian | `hrv` | Croatian | `ron` | Romanian | | `ben` | Bengali | `hin` | Hindi | `hun` | Hungarian | | `nan` | Hokkien | `nor` | Norwegian | `pan` | Punjabi | | `tha` | Thai | `tur` | Turkish | `vie` | Vietnamese | | `yue` | Cantonese | | | | | If not specified, the language will be automatically detected from the input text. ## Error Handling The SDK provides `TypecastAPIError` for handling API errors: ```typescript import { TypecastClient, TypecastAPIError } from '@neosapience/typecast-js'; try { const audio = await client.textToSpeech({ text: "Hello world", voice_id: "tc_672c5f5ce59fac2a48faeaee", model: "ssfm-v30" }); } catch (error) { if (error instanceof TypecastAPIError) { // TypecastAPIError exposes: statusCode, message, response switch (error.statusCode) { case 401: console.error('Invalid API key'); break; case 402: console.error('Insufficient credits'); break; case 422: console.error('Validation error:', error.response); break; case 429: console.error('Rate limit exceeded - please retry later'); break; default: console.error(`API error (${error.statusCode}):`, error.message); } } else { console.error('Unexpected error:', error); } } ``` ## TypeScript Support This SDK is written in TypeScript and provides full type definitions: ```typescript import type { TTSRequest, TTSResponse, TTSModel, LanguageCode, Prompt, PresetPrompt, SmartPrompt, Output, VoiceV2Response, VoicesV2Filter } from '@neosapience/typecast-js'; ``` ## Control silence duration Requires **0.4.13 or later**. `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. Pass these output settings to the corresponding request's `output`. For streaming, use the streaming output type. ```typescript const output = { remove_silence_ms: 300 }; ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Go The official Go library for the [Typecast API](https://typecast.ai). Convert text to lifelike speech using AI-powered voices. Compatible with Go 1.21 and later versions. Zero external dependencies - uses only the Go standard library. Typecast Go SDK Typecast Go SDK Source Code ## Installation ```bash go get github.com/neosapience/typecast-sdk/typecast-go ``` Latest registered version: **typecast-go/v0.3.14** via Go modules. Make sure you have **Go 1.21 or higher** installed. Check your version with `go version`. ## Quick Start ```go package main import ( "context" "os" typecast "github.com/neosapience/typecast-sdk/typecast-go" ) func main() { // Initialize client client := typecast.NewClient(&typecast.ClientConfig{ APIKey: "YOUR_API_KEY", }) ctx := context.Background() // Convert text to speech response, err := client.TextToSpeech(ctx, &typecast.TTSRequest{ VoiceID: "tc_672c5f5ce59fac2a48faeaee", // Find voice IDs at https://studio.typecast.ai/developers/api/voices Text: "Hello there! I'm your friendly text-to-speech agent.", Model: typecast.ModelSSFMV30, }) if err != nil { panic(err) } // Save audio file os.WriteFile("output.wav", response.AudioData, 0644) println("Audio saved! Format:", string(response.Format)) } ``` ## Features The Typecast Go SDK provides powerful features for text-to-speech conversion: - **Multiple Voice Models**: Support for `ssfm-v30` (latest) and `ssfm-v21` AI voice models - **Multi-language Support**: 35+ languages including English, Korean, Spanish, Japanese, Chinese, and more - **Emotion Control**: Preset emotions (normal, happy, sad, angry, whisper, toneup, tonedown) or smart context-aware inference - **Audio Customization**: Control loudness (LUFS -70 to 0), pitch (-12 to +12 semitones), tempo (0.5x to 2.0x), and format (WAV/MP3) - **Voice Discovery**: V2 Voices API with filtering by model, gender, age, and use cases - **Instant Voice Cloning**: Upload a WAV/MP3 sample and create a custom voice ID - **Context Support**: Full `context.Context` support for cancellation and timeouts - **Timestamp TTS**: Word- and character-level alignment data for subtitles, karaoke, and lip-sync - **Zero Dependencies**: Uses only the Go standard library - **Streaming**: Real-time chunked audio delivery for low-latency playback ## Voice Recommendations Use `RecommendVoices` when you know the desired style but not the exact `voice_id`. ```go voices, err := client.RecommendVoices( context.Background(), "warm female voice for a product tutorial", 3, ) if err != nil { panic(err) } for _, voice := range voices { fmt.Println(voice.VoiceID, voice.VoiceName, voice.Score) } ``` Recommendation results contain only `VoiceID`, `VoiceName`, and `Score`. Use `GetVoiceV2` or `GetVoicesV2` when you need detailed metadata such as supported models, emotions, gender, age, or use cases. ## Configuration Set your API key via environment variable or pass directly: ```go import typecast "github.com/neosapience/typecast-sdk/typecast-go" // Using environment variable (recommended) // export TYPECAST_API_KEY="your-api-key-here" client := typecast.NewClient(nil) // Or pass directly client := typecast.NewClient(&typecast.ClientConfig{ APIKey: "your-api-key-here", }) // With custom settings client := typecast.NewClient(&typecast.ClientConfig{ APIKey: "your-api-key-here", BaseURL: "https://api.typecast.ai", // optional Timeout: 60 * time.Second, // optional }) ``` When requests go through your own proxy, set `BaseURL` or `TYPECAST_API_HOST` to the proxy endpoint and omit `APIKey`. The SDK will not send the `X-API-KEY` header for empty or missing keys. Requests to the default Typecast host still require an API key. ```go Proxy without API key client := typecast.NewClient(&typecast.ClientConfig{ BaseURL: "https://your-proxy.example.com", }) ``` ### Environment Variables | Variable | Description | |----------|-------------| | `TYPECAST_API_KEY` | Your Typecast API key | | `TYPECAST_API_HOST` | Custom API base URL (optional) | ## Advanced Usage ### Emotion Control (ssfm-v30) ssfm-v30 offers two emotion control modes: **Preset** and **Smart**. Let the AI infer emotion from context: ```go response, err := client.TextToSpeech(ctx, &typecast.TTSRequest{ VoiceID: "tc_672c5f5ce59fac2a48faeaee", Text: "Everything is going to be okay.", Model: typecast.ModelSSFMV30, Prompt: &typecast.SmartPrompt{ EmotionType: "smart", PreviousText: "I just got the best news!", // Optional context NextText: "I can't wait to celebrate!", // Optional context }, }) ``` Explicitly set emotion with preset values: ```go intensity := 1.5 response, err := client.TextToSpeech(ctx, &typecast.TTSRequest{ VoiceID: "tc_672c5f5ce59fac2a48faeaee", Text: "I am so excited to show you these features!", Model: typecast.ModelSSFMV30, Prompt: &typecast.PresetPrompt{ EmotionType: "preset", EmotionPreset: typecast.EmotionHappy, // normal, happy, sad, angry, whisper, toneup, tonedown EmotionIntensity: &intensity, // Range: 0.0 to 2.0 }, }) ``` ### Audio Customization Control loudness, pitch, tempo, and output format: ```go lufs := -14.0 pitch := 2 tempo := 1.2 response, err := client.TextToSpeech(ctx, &typecast.TTSRequest{ Text: "Customized audio output!", Model: typecast.ModelSSFMV30, VoiceID: "tc_672c5f5ce59fac2a48faeaee", Output: &typecast.Output{ TargetLUFS: &lufs, // Range: -70 to 0 (LUFS) AudioPitch: &pitch, // Range: -12 to +12 semitones AudioTempo: &tempo, // Range: 0.5x to 2.0x AudioFormat: typecast.AudioFormatMP3, // Options: wav, mp3 }, }) ``` ### Generate audio to a file Use `GenerateToFile` when you want the SDK to synthesize speech and write the audio bytes directly to a local file. The model defaults to `ssfm-v30`, and `.mp3` / `.wav` extensions infer the output format when `Output.AudioFormat` is not set. Browse available voice IDs on the [Voices](https://studio.typecast.ai/developers/api/voices) page. ```go _, err := client.GenerateToFile(ctx, "output.mp3", typecast.GenerateToFileRequest{ Text: "Hello from Typecast.", VoiceID: "tc_672c5f5ce59fac2a48faeaee", // Find voice IDs at https://studio.typecast.ai/developers/api/voices }) if err != nil { panic(err) } ``` ### Text pauses Use text pause markup when you only need silent gaps inside one composed text segment. Put `<|5s|>`, `<|1s|>`, `<|0.3s|>`, or `<|0.34413s|>` directly in the text. The value is interpreted as seconds and must end with `s`. This keeps the pause expression visible in plain text without adding separate pause calls. ```go audio, err := client.ComposeSpeech(). Defaults(typecast.ComposerSettings{VoiceID: "tc_672c5f5ce59fac2a48faeaee", Model: typecast.TTSModelSSFMV30}). Say("Hello<|5s|>Nice to meet you<|1s|>Today<|2s|>how does the weather feel?"). Generate(ctx) ``` ### Multi-speaker composition Use the composer chaining API when one output file needs different voices or per-segment options such as pitch, tempo, prompt, or seed. The composer sends segments to `POST /v1/text-to-speech/compose`, which returns WAV or MP3 directly. Set silence removal explicitly on TTS segments; explicit pauses are preserved. ```go package main import ( "context" "os" typecast "github.com/neosapience/typecast-sdk/typecast-go" ) func main() { client := typecast.NewClient("YOUR_API_KEY") response, err := client.ComposeSpeech(). Defaults(typecast.ComposerSettings{VoiceID: "tc_672c5f5ce59fac2a48faeaee", Model: typecast.ModelSSFMV30}). Say("Hello there"). Pause(5). SayWith("Nice to meet you", typecast.ComposerSettings{VoiceID: "tc_60e5426de8b95f1d3000d7b5", Output: &typecast.Output{AudioPitch: 2}}). Say("Today"). Pause(2). Say("How does the weather feel?"). Generate(context.Background()) if err != nil { panic(err) } _ = os.WriteFile("conversation.wav", response.AudioData, 0644) } ``` ### Voice Discovery (V2 API) List and filter available voices with enhanced metadata: ```go // Get all voices voices, err := client.GetVoicesV2(ctx, nil) // Filter by criteria voices, err := client.GetVoicesV2(ctx, &typecast.VoicesV2Filter{ Model: typecast.ModelSSFMV30, Gender: typecast.GenderFemale, Age: typecast.AgeYoungAdult, }) // Display voice info for _, voice := range voices { fmt.Printf("ID: %s, Name: %s\n", voice.VoiceID, voice.VoiceName) if voice.Gender != nil { fmt.Printf("Gender: %s\n", *voice.Gender) } if voice.Age != nil { fmt.Printf("Age: %s\n", *voice.Age) } for _, model := range voice.Models { fmt.Printf("Model: %s, Emotions: %v\n", model.Version, model.Emotions) } } // Get specific voice details voice, err := client.GetVoiceV2(ctx, "tc_672c5f5ce59fac2a48faeaee") ``` ### Multilingual Content The SDK supports 35+ languages with automatic language detection: ```go // Auto-detect language (recommended) response, err := client.TextToSpeech(ctx, &typecast.TTSRequest{ VoiceID: "tc_672c5f5ce59fac2a48faeaee", Text: "こんにちは。お元気ですか。", Model: typecast.ModelSSFMV30, }) // Or specify language explicitly response, err := client.TextToSpeech(ctx, &typecast.TTSRequest{ VoiceID: "tc_672c5f5ce59fac2a48faeaee", Text: "안녕하세요. 반갑습니다.", Model: typecast.ModelSSFMV30, Language: "kor", // ISO 639-3 language code }) ``` ### Streaming Stream audio chunks in real-time for low-latency playback: ```go // Stream and extract raw PCM (skip 44-byte WAV header) reader, _ := client.TextToSpeechStream(context.Background(), request) defer reader.Close() buf := make([]byte, 4096) first := true for { n, err := reader.Read(buf) if n > 0 { data := buf[:n] if first { data = data[44:] // Skip WAV header first = false } // data is raw 16-bit mono PCM at 32000 Hz // Feed to your audio output (e.g. oto, portaudio) _ = data } if err != nil { break } } ``` **WAV streaming format:** 32000 Hz, 16-bit, mono PCM. The first chunk includes a 44-byte WAV header (size = `0xFFFFFFFF`); subsequent chunks are raw PCM only. For MP3: 320 kbps, 44100 Hz, each chunk is independently decodable. ## Timestamp TTS `TextToSpeechWithTimestamps()` wraps `POST /v1/text-to-speech/with-timestamps` and returns the audio together with per-word and per-character alignment data - useful for karaoke highlights, subtitle generation, and lip-sync applications. ### Basic Usage ```go package main import ( "context" "fmt" "os" typecast "github.com/neosapience/typecast-sdk/typecast-go" ) func main() { client := typecast.NewClient(&typecast.ClientConfig{APIKey: "YOUR_API_KEY"}) ctx := context.Background() result, err := client.TextToSpeechWithTimestamps(ctx, &typecast.TTSRequestWithTimestamps{ VoiceID: "tc_60e5426de8b95f1d3000d7b5", Text: "Hello. How are you?", Model: typecast.ModelSSFMV30, }) if err != nil { panic(err) } os.WriteFile("output.wav", result.AudioBytes(), 0644) fmt.Printf("Duration: %.3fs\n", result.AudioDuration) for _, w := range result.Words { fmt.Printf(" [%.3fs – %.3fs] %s\n", w.StartTime, w.EndTime, w.Text) } } ``` ### Granularity Pass `Granularity: typecast.GranularityWord` (default) or `Granularity: typecast.GranularityChar` to control the alignment unit. ```go // Character-level alignment - required for Japanese / Chinese result, err := client.TextToSpeechWithTimestamps(ctx, &typecast.TTSRequestWithTimestamps{ VoiceID: "tc_60e5426de8b95f1d3000d7b5", Text: "Hello. How are you?", Model: typecast.ModelSSFMV30, Granularity: typecast.GranularityChar, }) ``` ### Subtitle Export ```go srt, _ := result.ToSrt() os.WriteFile("output.srt", []byte(srt), 0644) vtt, _ := result.ToVtt() os.WriteFile("output.vtt", []byte(vtt), 0644) ``` **Japanese / Chinese:** Word-level segmentation is not meaningful for languages without whitespace delimiters (jpn, zho). Use `GranularityChar` for these languages to get character-level alignment. ## Instant Voice Cloning Clone a custom voice from a short audio sample, then pass the returned `uc_` voice ID directly to TTS. ```go package main import ( "context" "os" typecast "github.com/neosapience/typecast-sdk/typecast-go" ) func main() { client := typecast.NewClient(&typecast.ClientConfig{APIKey: "YOUR_API_KEY"}) ctx := context.Background() audioBytes, err := os.ReadFile("sample.wav") if err != nil { panic(err) } voice, err := client.CloneVoice(ctx, audioBytes, "sample.wav", "MyVoice", "ssfm-v30") if err != nil { panic(err) } response, err := client.TextToSpeech(ctx, &typecast.TTSRequest{ VoiceID: voice.VoiceID, Text: "Hello from my cloned voice!", Model: typecast.ModelSSFMV30, }) if err != nil { panic(err) } os.WriteFile("output.wav", response.AudioData, 0644) client.DeleteVoice(ctx, voice.VoiceID) } ``` Voice cloning audio must be **25 MB or smaller**, the audio duration must be **5-150 seconds**, and the custom voice name must be **1-30 characters**. ## Supported Languages The SDK supports 35+ languages with automatic language detection: | Code | Language | Code | Language | Code | Language | |------|----------|------|----------|------|----------| | `eng` | English | `jpn` | Japanese | `ukr` | Ukrainian | | `kor` | Korean | `ell` | Greek | `ind` | Indonesian | | `spa` | Spanish | `tam` | Tamil | `dan` | Danish | | `deu` | German | `tgl` | Tagalog | `swe` | Swedish | | `fra` | French | `fin` | Finnish | `msa` | Malay | | `ita` | Italian | `zho` | Chinese | `ces` | Czech | | `pol` | Polish | `slk` | Slovak | `por` | Portuguese | | `nld` | Dutch | `ara` | Arabic | `bul` | Bulgarian | | `rus` | Russian | `hrv` | Croatian | `ron` | Romanian | | `ben` | Bengali | `hin` | Hindi | `hun` | Hungarian | | `nan` | Hokkien | `nor` | Norwegian | `pan` | Punjabi | | `tha` | Thai | `tur` | Turkish | `vie` | Vietnamese | | `yue` | Cantonese | | | | | If not specified, the language will be automatically detected from the input text. ## Error Handling The SDK provides an `APIError` type with helper methods for handling specific errors: ```go import typecast "github.com/neosapience/typecast-sdk/typecast-go" response, err := client.TextToSpeech(ctx, request) if err != nil { if apiErr, ok := err.(*typecast.APIError); ok { fmt.Printf("Error %d: %s\n", apiErr.StatusCode, apiErr.Message) // Handle specific errors switch { case apiErr.IsUnauthorized(): // 401: Invalid API key case apiErr.IsForbidden(): // 403: Access denied case apiErr.IsPaymentRequired(): // 402: Insufficient credits case apiErr.IsNotFound(): // 404: Resource not found case apiErr.IsValidationError(): // 422: Validation error case apiErr.IsRateLimited(): // 429: Rate limit exceeded case apiErr.IsServerError(): // 5xx: Server error case apiErr.IsBadRequest(): // 400: Bad request } } } ``` ### Error Types | Method | Status Code | Description | |--------|-------------|-------------| | `IsBadRequest()` | 400 | Invalid request parameters | | `IsUnauthorized()` | 401 | Invalid or missing API key | | `IsPaymentRequired()` | 402 | Insufficient credits | | `IsForbidden()` | 403 | Access denied | | `IsNotFound()` | 404 | Resource not found | | `IsValidationError()` | 422 | Validation error | | `IsRateLimited()` | 429 | Rate limit exceeded | | `IsServerError()` | 5xx | Server error | ## Context and Timeouts The SDK fully supports Go's `context.Context` for cancellation and timeouts: ```go // With timeout ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() response, err := client.TextToSpeech(ctx, request) if err != nil { if ctx.Err() == context.DeadlineExceeded { fmt.Println("Request timed out") } } // With cancellation ctx, cancel := context.WithCancel(context.Background()) go func() { time.Sleep(5 * time.Second) cancel() // Cancel after 5 seconds }() response, err := client.TextToSpeech(ctx, request) ``` ## API Reference ### Client Methods | Method | Description | |--------|-------------| | `TextToSpeech(ctx, request)` | Convert text to speech audio | | `GenerateToFile(ctx, path, request)` | Generate speech and save it directly to a local file | | `CloneVoice(ctx, audio, filename, name, model)` | Create a custom voice via instant cloning | | `DeleteVoice(ctx, voiceID)` | Delete a custom cloned voice | | `GetVoicesV2(ctx, filter)` | Get available voices with filtering | | `GetVoiceV2(ctx, voiceID)` | Get a specific voice by ID | | `GetVoices(ctx, model)` | Get voices (V1 API, deprecated) | | `GetVoice(ctx, voiceID, model)` | Get voice (V1 API, deprecated) | ### TTSRequest Fields | Field | Type | Required | Description | |-------|------|----------|-------------| | `VoiceID` | `string` | ✓ | Voice ID (format: `tc_*` or `uc_*`) | | `Text` | `string` | ✓ | Text to synthesize (max 2000 chars) | | `Model` | `TTSModel` | ✓ | TTS model (`ModelSSFMV21` or `ModelSSFMV30`) | | `Language` | `string` | | ISO 639-3 code (auto-detected if omitted) | | `Prompt` | `*Prompt` / `*PresetPrompt` / `*SmartPrompt` | | Emotion settings | | `Output` | `*Output` | | Audio output settings | | `Seed` | `*uint32` | | Unsigned integer seed for reproducibility (≥ 0) | ### TTSResponse Fields | Field | Type | Description | |-------|------|-------------| | `AudioData` | `[]byte` | Generated audio data | | `Duration` | `float64` | Audio duration in seconds | | `Format` | `AudioFormat` | Audio format (`wav` or `mp3`) | ### Constants #### Models | Constant | Value | Description | |----------|-------|-------------| | `ModelSSFMV30` | `ssfm-v30` | Latest model with improved prosody | | `ModelSSFMV21` | `ssfm-v21` | Stable production model | #### Emotion Presets | Constant | ssfm-v21 | ssfm-v30 | |----------|----------|----------| | `EmotionNormal` | ✓ | ✓ | | `EmotionHappy` | ✓ | ✓ | | `EmotionSad` | ✓ | ✓ | | `EmotionAngry` | ✓ | ✓ | | `EmotionWhisper` | ✗ | ✓ | | `EmotionToneUp` | ✗ | ✓ | | `EmotionToneDown` | ✗ | ✓ | #### Audio Formats | Constant | Value | Description | |----------|-------|-------------| | `AudioFormatWAV` | `wav` | Uncompressed PCM audio | | `AudioFormatMP3` | `mp3` | Compressed MP3 audio | ## Control silence duration Requires **0.3.14 or later**. `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. Pass these output settings to the corresponding request's `output`. For streaming, use the streaming output type. ```go remainingSilence := 300 output := &typecast.Output{RemoveSilenceMS: &remainingSilence} streamOutput := &typecast.OutputStream{RemoveSilenceMS: &remainingSilence} ``` `RemoveSilenceMS` is a pointer: pass an integer address to distinguish explicit `0` from `nil`. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Rust The official Rust library for the [Typecast API](https://typecast.ai). Convert text to lifelike speech using AI-powered voices. Built with async/await support using Tokio runtime. Works with Cargo package manager. Typecast Rust SDK Typecast Rust SDK Source Code ## Installation Add the following to your `Cargo.toml`: ```toml [dependencies] typecast-rust = "0.3.15" tokio = { version = "1", features = ["full"] } ``` Or use Cargo to add the dependency: ```bash cargo add typecast-rust tokio --features tokio/full ``` Latest registered version: **0.3.15** on crates.io. Make sure you have **version 0.3.15 or higher** installed. Check your `Cargo.toml` if you need to update. ## Quick Start ```rust use typecast_rust::{TypecastClient, TTSRequest, TTSModel}; use std::fs; #[tokio::main] async fn main() -> Result<(), Box> { // Initialize client (reads TYPECAST_API_KEY from environment) let client = TypecastClient::from_env()?; // Convert text to speech let request = TTSRequest::new( "tc_672c5f5ce59fac2a48faeaee", "Hello there! I'm your friendly text-to-speech agent.", TTSModel::SsfmV30, ); let response = client.text_to_speech(&request).await?; // Save audio file fs::write("output.wav", &response.audio_data)?; println!( "Audio saved! Duration: {:.2}s, Format: {:?}", response.duration, response.format ); Ok(()) } ``` ## Features The Typecast Rust SDK provides powerful features for text-to-speech conversion: - **Multiple Voice Models**: Support for `ssfm-v30` (latest) and `ssfm-v21` AI voice models - **Multi-language Support**: 35+ languages including English, Korean, Spanish, Japanese, Chinese, and more - **Emotion Control**: Preset emotions (normal, happy, sad, angry, whisper, toneup, tonedown) or smart context-aware inference - **Audio Customization**: Control loudness (LUFS -70 to 0), pitch (-12 to +12 semitones), tempo (0.5x to 2.0x), and format (WAV/MP3) - **Voice Discovery**: V2 Voices API with filtering by model, gender, age, and use cases - **Instant Voice Cloning**: Upload a WAV/MP3 sample and create a custom voice ID - **Builder Pattern**: Fluent API with method chaining for easy request construction - **Async/Await**: Built on Tokio for efficient asynchronous operations - **Timestamp TTS**: Word- and character-level alignment data for subtitles, karaoke, and lip-sync - **Comprehensive Error Handling**: Typed error enum with pattern matching support - **Streaming**: Real-time chunked audio delivery for low-latency playback ## Voice Recommendations Use `recommend_voices` when you know the desired style but not the exact `voice_id`. ```rust let voices = client .recommend_voices("warm female voice for a product tutorial", Some(3)) .await?; for voice in voices { println!("{} {} {}", voice.voice_id, voice.voice_name, voice.score); } ``` Recommendation results contain only `voice_id`, `voice_name`, and `score`. Use `get_voice_v3` or `get_voices_v3` when you need current voice metadata. ## Configuration Set your API key via environment variable or constructor: ```rust use typecast_rust::{TypecastClient, ClientConfig}; use std::time::Duration; // Using environment variable (recommended) // export TYPECAST_API_KEY="your-api-key-here" let client = TypecastClient::from_env()?; // Or pass directly let client = TypecastClient::with_api_key("your-api-key-here")?; // Or with custom configuration let config = ClientConfig::new("your-api-key-here") .base_url("https://api.typecast.ai") .timeout(Duration::from_secs(120)); let client = TypecastClient::new(config)?; ``` When requests go through your own proxy, set `base_url` to the proxy endpoint and omit the API key. The SDK will not send the `X-API-KEY` header for empty or missing keys. Requests to the default Typecast host still require an API key. ```rust Proxy without API key let config = ClientConfig::new("") .base_url("https://your-proxy.example.com"); let client = TypecastClient::new(config)?; ``` ### Environment File Create a `.env` file in your project root: ```bash TYPECAST_API_KEY=your-api-key-here ``` Use the `dotenvy` crate to load environment variables from `.env` files. ## Advanced Usage ### Emotion Control (ssfm-v30) ssfm-v30 offers two emotion control modes: **Preset** and **Smart**. Let the AI infer emotion from context: ```rust use typecast_rust::{TTSRequest, TTSModel, SmartPrompt}; let request = TTSRequest::new( "tc_672c5f5ce59fac2a48faeaee", "Everything is going to be okay.", TTSModel::SsfmV30, ) .prompt( SmartPrompt::new() .previous_text("I just got the best news!") // Optional context .next_text("I can't wait to celebrate!") // Optional context ); let response = client.text_to_speech(&request).await?; ``` Explicitly set emotion with preset values: ```rust use typecast_rust::{TTSRequest, TTSModel, PresetPrompt, EmotionPreset}; let request = TTSRequest::new( "tc_672c5f5ce59fac2a48faeaee", "I am so excited to show you these features!", TTSModel::SsfmV30, ) .prompt( PresetPrompt::new() .emotion_preset(EmotionPreset::Happy) // Normal, Happy, Sad, Angry, Whisper, ToneUp, ToneDown .emotion_intensity(1.5) // Range: 0.0 to 2.0 ); let response = client.text_to_speech(&request).await?; ``` ### Audio Customization Control loudness, pitch, tempo, and output format: ```rust use typecast_rust::{TTSRequest, TTSModel, Output, AudioFormat}; let request = TTSRequest::new( "tc_672c5f5ce59fac2a48faeaee", "Customized audio output!", TTSModel::SsfmV30, ) .output( Output::new() .target_lufs(-14.0) // Range: -70 to 0 (LUFS) .audio_pitch(2) // Range: -12 to +12 semitones .audio_tempo(1.2) // Range: 0.5x to 2.0x .audio_format(AudioFormat::Mp3) // Options: Wav, Mp3 ) .seed(42); // Unsigned seed for reproducible results let response = client.text_to_speech(&request).await?; fs::write("output.mp3", &response.audio_data)?; println!("Duration: {:.2}s, Format: {:?}", response.duration, response.format); ``` ### Generate audio to a file Use `generate_to_file` when you want the SDK to synthesize speech and write the audio bytes directly to a local file. The model defaults to `ssfm-v30`, and `.mp3` / `.wav` extensions infer the output format when no output format is set. Browse available voice IDs on the [Voices](https://studio.typecast.ai/developers/api/voices) page. ```rust client.generate_to_file( "output.mp3", GenerateToFileRequest::new("tc_672c5f5ce59fac2a48faeaee", "Hello from Typecast."), // Find voice IDs at https://studio.typecast.ai/developers/api/voices ).await?; ``` ### Text pauses Use text pause markup when you only need silent gaps inside one composed text segment. Put `<|5s|>`, `<|1s|>`, `<|0.3s|>`, or `<|0.34413s|>` directly in the text. The value is interpreted as seconds and must end with `s`. This keeps the pause expression visible in plain text without adding separate pause calls. ```rust let audio = client .compose_speech() .defaults(ComposerSettings::new().voice_id("tc_672c5f5ce59fac2a48faeaee").model(TtsModel::SsfmV30)) .say("Hello<|5s|>Nice to meet you<|1s|>Today<|2s|>how does the weather feel?") .generate() .await?; ``` ### Multi-speaker composition Use the composer chaining API when one output file needs different voices or per-segment options such as pitch, tempo, prompt, or seed. The composer sends segments to `POST /v1/text-to-speech/compose`, which returns WAV or MP3 directly. Set silence removal explicitly on TTS segments; explicit pauses are preserved. ```rust use typecast_rust::{ComposerSettings, Output, TTSModel, TypecastClient}; #[tokio::main] async fn main() -> Result<(), Box> { let client = TypecastClient::new("YOUR_API_KEY")?; let audio = client .compose_speech() .defaults(ComposerSettings::new().voice_id("tc_672c5f5ce59fac2a48faeaee").model(TTSModel::SSFM_V30)) .say("Hello there") .pause(5.0) .say_with( "Nice to meet you", ComposerSettings::new().voice_id("tc_60e5426de8b95f1d3000d7b5").output(Output { audio_pitch: Some(2), ..Default::default() }), ) .say("Today") .pause(2) .say("How does the weather feel?") .generate() .await?; std::fs::write("conversation.wav", audio.audio_data)?; Ok(()) } ``` ## Control silence duration Requires **0.3.15 or later**. `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. Pass these output settings to the corresponding request's `output`. For streaming, use the streaming output type. ```rust use typecast_rust::{Output, OutputStream}; let output = Output::new().remove_silence_ms(300); let stream_output = OutputStream::new().remove_silence_ms(300); ``` ### Voice Discovery (V3 API) List and filter available voices with enhanced metadata: ```rust use typecast_rust::{TypecastClient, VoicesV2Filter, TTSModel, Gender, Age}; // Get all voices let voices = client.get_voices_v3(None).await?; // Filter by criteria let filter = VoicesV2Filter::new() .model(TTSModel::SsfmV30) .gender(Gender::Female) .age(Age::YoungAdult); let filtered = client.get_voices_v3(Some(filter)).await?; // Display voice info for voice in &voices { println!("ID: {}, Name: {}", voice.voice_id, voice.voice_name.eng); println!("Gender: {:?}, Age: {:?}", voice.gender, voice.age); for model in &voice.models { println!("Model: {:?}, Emotions: {:?}", model.version, model.emotions); } if let Some(use_cases) = &voice.use_cases { println!("Use cases: {}", use_cases.join(", ")); } } // Get a specific voice by ID let voice = client.get_voice_v3("tc_672c5f5ce59fac2a48faeaee").await?; println!("Voice: {} ({:?})", voice.voice_name.eng, voice.gender); ``` ### Multilingual Content The SDK supports 35+ languages with automatic language detection: ```rust // Auto-detect language (recommended) let request = TTSRequest::new( "tc_672c5f5ce59fac2a48faeaee", "こんにちは。お元気ですか。", TTSModel::SsfmV30, ); let response = client.text_to_speech(&request).await?; // Or specify language explicitly let korean_request = TTSRequest::new( "tc_672c5f5ce59fac2a48faeaee", "안녕하세요. 반갑습니다.", TTSModel::SsfmV30, ) .language("kor"); // ISO 639-3 language code let korean_response = client.text_to_speech(&korean_request).await?; ``` ### Streaming Stream audio chunks in real-time for low-latency playback: ```rust // Stream and extract raw PCM (skip 44-byte WAV header) let mut stream = client.text_to_speech_stream(&request).await?; let mut first = true; while let Some(chunk) = stream.next().await { let bytes = chunk?; let pcm = if first { first = false; &bytes[44..] // Skip WAV header } else { &bytes }; // pcm is raw 16-bit mono PCM at 32000 Hz // Feed to your audio output (e.g. rodio, cpal) } ``` **WAV streaming format:** 32000 Hz, 16-bit, mono PCM. The first chunk includes a 44-byte WAV header (size = `0xFFFFFFFF`); subsequent chunks are raw PCM only. For MP3: 320 kbps, 44100 Hz, each chunk is independently decodable. Requires `futures-util` for `StreamExt`. ## Timestamp TTS `text_to_speech_with_timestamps()` wraps `POST /v1/text-to-speech/with-timestamps` and returns the audio together with per-word and per-character alignment data - useful for karaoke highlights, subtitle generation, and lip-sync applications. ### Basic Usage ```rust use typecast_rust::{TypecastClient, TTSRequestWithTimestamps, TTSModel}; use std::fs; #[tokio::main] async fn main() -> Result<(), Box> { let client = TypecastClient::from_env()?; let request = TTSRequestWithTimestamps::new( "tc_60e5426de8b95f1d3000d7b5", "Hello. How are you?", TTSModel::SsfmV30, ); let result = client.text_to_speech_with_timestamps(&request).await?; fs::write("output.wav", result.audio_bytes())?; println!("Duration: {:.3}s", result.audio_duration); for word in &result.words { println!(" [{:.3}s – {:.3}s] {}", word.start_time, word.end_time, word.text); } Ok(()) } ``` ### Granularity Chain `.granularity(Granularity::Word)` (default) or `.granularity(Granularity::Char)` to control the alignment unit. ```rust use typecast_rust::Granularity; let request = TTSRequestWithTimestamps::new( "tc_60e5426de8b95f1d3000d7b5", "Hello. How are you?", TTSModel::SsfmV30, ) .granularity(Granularity::Char); // required for Japanese / Chinese ``` ### Subtitle Export ```rust fs::write("output.srt", result.to_srt())?; fs::write("output.vtt", result.to_vtt())?; ``` **Japanese / Chinese:** Word-level segmentation is not meaningful for languages without whitespace delimiters (jpn, zho). Use `Granularity::Char` for these languages to get character-level alignment. ## Instant Voice Cloning Clone a custom voice from a short audio sample, then pass the returned `uc_` voice ID directly to TTS. ```rust use typecast_rust::{TypecastClient, TTSModel, TTSRequest}; let client = TypecastClient::from_env()?; let audio = std::fs::read("sample.wav")?; let voice = client .clone_voice(audio, "sample.wav", "My Voice", "ssfm-v30") .await?; let request = TTSRequest::new( &voice.voice_id, "Hello from my cloned voice!", TTSModel::SsfmV30, ); let response = client.text_to_speech(&request).await?; std::fs::write("output.wav", &response.audio_data)?; client.delete_voice(&voice.voice_id).await?; ``` Voice cloning audio must be **25 MB or smaller**, the audio duration must be **5-150 seconds**, and the custom voice name must be **1-30 characters**. ## Supported Languages The SDK supports 35+ languages with automatic language detection: | Code | Language | Code | Language | Code | Language | |------|----------|------|----------|------|----------| | `eng` | English | `jpn` | Japanese | `ukr` | Ukrainian | | `kor` | Korean | `ell` | Greek | `ind` | Indonesian | | `spa` | Spanish | `tam` | Tamil | `dan` | Danish | | `deu` | German | `tgl` | Tagalog | `swe` | Swedish | | `fra` | French | `fin` | Finnish | `msa` | Malay | | `ita` | Italian | `zho` | Chinese | `ces` | Czech | | `pol` | Polish | `slk` | Slovak | `por` | Portuguese | | `nld` | Dutch | `ara` | Arabic | `bul` | Bulgarian | | `rus` | Russian | `hrv` | Croatian | `ron` | Romanian | | `ben` | Bengali | `hin` | Hindi | `hun` | Hungarian | | `nan` | Hokkien | `nor` | Norwegian | `pan` | Punjabi | | `tha` | Thai | `tur` | Turkish | `vie` | Vietnamese | | `yue` | Cantonese | | | | | If not specified, the language will be automatically detected from the input text. ## Error Handling The SDK provides a typed error enum for handling API errors with pattern matching: ```rust use typecast_rust::{TypecastClient, TTSRequest, TTSModel, TypecastError}; let request = TTSRequest::new("voice_id", "Hello", TTSModel::SsfmV30); match client.text_to_speech(&request).await { Ok(response) => { println!("Success! Duration: {:.2}s", response.duration); } Err(TypecastError::Unauthorized { detail }) => { // 401: Invalid API key eprintln!("Invalid API key: {}", detail); } Err(TypecastError::PaymentRequired { detail }) => { // 402: Insufficient credits eprintln!("Insufficient credits: {}", detail); } Err(TypecastError::NotFound { detail }) => { // 404: Resource not found eprintln!("Voice not found: {}", detail); } Err(TypecastError::RateLimited { detail }) => { // 429: Rate limit exceeded eprintln!("Rate limit exceeded - please retry later: {}", detail); } Err(TypecastError::ServerError { detail }) => { // 500: Server error eprintln!("Server error: {}", detail); } Err(e) => { eprintln!("Error: {}", e); } } ``` ### Error Types | Error Variant | Status Code | Description | |---------------|-------------|-------------| | `BadRequest` | 400 | Invalid request parameters | | `Unauthorized` | 401 | Invalid or missing API key | | `PaymentRequired` | 402 | Insufficient credits | | `Forbidden` | 403 | Access denied | | `NotFound` | 404 | Resource not found | | `ValidationError` | 422 | Validation error | | `RateLimited` | 429 | Rate limit exceeded | | `ServerError` | 500 | Server error | | `HttpError` | - | HTTP client error | | `JsonError` | - | JSON serialization error | ### Helper Methods ```rust if let Err(e) = result { if e.is_unauthorized() { println!("Check your API key"); } else if e.is_rate_limited() { println!("Wait and retry"); } else if e.is_server_error() { println!("Server issue, try again later"); } if let Some(code) = e.status_code() { println!("HTTP status: {}", code); } } ``` ## API Reference ### TypecastClient Methods | Method | Description | |--------|-------------| | `from_env()` | Create client from environment variables | | `with_api_key(key)` | Create client with API key | | `new(config)` | Create client with custom configuration | | `text_to_speech(&request)` | Convert text to speech audio | | `generate_to_file(path, request)` | Generate speech and save it directly to a local file | | `clone_voice(audio, filename, name, model)` | Create a custom voice via instant cloning | | `delete_voice(voice_id)` | Delete a custom cloned voice | | `get_voices_v3(filter)` | Get available voices with optional filter | | `get_voice_v3(voice_id)` | Get a specific voice by ID | ### TTSRequest Fields | Field | Type | Required | Description | |-------|------|----------|-------------| | `voice_id` | `String` | ✓ | Voice ID (format: `tc_*` or `uc_*`) | | `text` | `String` | ✓ | Text to synthesize (max 2000 chars) | | `model` | `TTSModel` | ✓ | TTS model (`SsfmV21` or `SsfmV30`) | | `language` | `Option` | | ISO 639-3 code (auto-detected if omitted) | | `prompt` | `Option` | | Emotion settings (Prompt/PresetPrompt/SmartPrompt) | | `output` | `Option` | | Audio output settings | | `seed` | `Option` | | Unsigned integer seed for reproducibility (≥ 0) | ### TTSResponse Fields | Field | Type | Description | |-------|------|-------------| | `audio_data` | `Vec` | Generated audio data | | `duration` | `f64` | Audio duration in seconds | | `format` | `AudioFormat` | Audio format (`Wav` or `Mp3`) | ## Complete Example ```rust use typecast_rust::{ TypecastClient, TTSRequest, TTSModel, PresetPrompt, EmotionPreset, Output, AudioFormat, VoicesV2Filter, Gender, }; use std::fs; use std::error::Error; #[tokio::main] async fn main() -> Result<(), Box> { // Initialize client let client = TypecastClient::from_env()?; // Discover voices let filter = VoicesV2Filter::new() .model(TTSModel::SsfmV30) .gender(Gender::Female); let voices = client.get_voices_v3(Some(filter)).await?; println!("Found {} female voices", voices.len()); // Use first voice if let Some(voice) = voices.first() { let request = TTSRequest::new( &voice.voice_id, "Welcome to Typecast! This is a demonstration of our text-to-speech API.", TTSModel::SsfmV30, ) .language("eng") .prompt( PresetPrompt::new() .emotion_preset(EmotionPreset::Happy) .emotion_intensity(1.2) ) .output( Output::new() .target_lufs(-14.0) .audio_format(AudioFormat::Mp3) ); let response = client.text_to_speech(&request).await?; fs::write("welcome.mp3", &response.audio_data)?; println!("Saved welcome.mp3 ({:.2}s)", response.duration); } Ok(()) } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # C#/.NET The official C# library for the [Typecast API](https://typecast.ai). Convert text to lifelike speech using AI-powered voices. Supports .NET Standard 2.0+, .NET 6+, Unity (via NuGetForUnity), and Blazor applications. Full async/await support with synchronous alternatives. Typecast C# SDK on NuGet Typecast C# SDK Source Code ## Prerequisites ### Installing .NET SDK **Using Homebrew (Recommended)** ```bash # Install .NET 8 SDK brew install dotnet@8 # Add to PATH export PATH="/opt/homebrew/opt/dotnet@8/bin:$PATH" # Verify installation dotnet --version ``` **Using Official Installer** Download from [dotnet.microsoft.com/download](https://dotnet.microsoft.com/download) and run the `.pkg` installer. **Using winget** ```powershell winget install Microsoft.DotNet.SDK.8 dotnet --version ``` **Using Chocolatey** ```powershell choco install dotnet-sdk dotnet --version ``` Or download from [dotnet.microsoft.com/download](https://dotnet.microsoft.com/download). ```bash # Ubuntu/Debian wget https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb sudo dpkg -i packages-microsoft-prod.deb sudo apt-get update sudo apt-get install -y dotnet-sdk-8.0 dotnet --version ``` ## Installation ```bash dotnet add package typecast-csharp ``` ```powershell Install-Package typecast-csharp ``` 1. Install [NuGetForUnity](https://github.com/GlitchEnzo/NuGetForUnity): - Open Package Manager (Window > Package Manager) - Click "+" > "Add package from git URL" - Enter: `https://github.com/GlitchEnzo/NuGetForUnity.git?path=/src/NuGetForUnity` 2. Open NuGet window (NuGet > Manage NuGet Packages) 3. Search for "typecast-csharp" and install Latest registered version: **0.3.13** on NuGet. You can check with `dotnet list package`. Update with `dotnet add package typecast-csharp` to get the latest version. ## Quick Start ```csharp using Typecast; using Typecast.Models; // Initialize client using var client = new TypecastClient("YOUR_API_KEY"); // Convert text to speech var request = new TTSRequest( text: "Hello there! I'm your friendly text-to-speech agent.", voiceId: "tc_672c5f5ce59fac2a48faeaee", model: TTSModel.SsfmV30 ); var response = await client.TextToSpeechAsync(request); // Save audio file await response.SaveToFileAsync("output.wav"); Console.WriteLine($"Audio saved! Duration: {response.Duration}s, Format: {response.Format}"); ``` ## Features The Typecast C# SDK provides powerful features for text-to-speech conversion: - **Multiple Voice Models**: Support for `ssfm-v30` (latest) and `ssfm-v21` AI voice models - **Multi-language Support**: 35+ languages including English, Korean, Spanish, Japanese, Chinese, and more - **Emotion Control**: Preset emotions (normal, happy, sad, angry, whisper, toneup, tonedown) or smart context-aware inference - **Audio Customization**: Control loudness (LUFS -70 to 0), pitch (-12 to +12 semitones), tempo (0.5x to 2.0x), and format (WAV/MP3) - **Voice Discovery**: V2 Voices API with filtering by model, gender, age, and use cases - **Instant Voice Cloning**: Upload a WAV/MP3 sample and create a custom voice ID - **Timestamp TTS**: Word- and character-level alignment data for subtitles, karaoke, and lip-sync - **Unity Support**: Compatible with Unity via NuGetForUnity - **Blazor Support**: Works with Blazor Server and WebAssembly - **Async/Sync APIs**: Full async/await support with synchronous alternatives - **Streaming**: Real-time chunked audio delivery for low-latency playback ## Voice Recommendations Use `RecommendVoicesAsync` when you know the desired style but not the exact `voice_id`. ```csharp var voices = await client.RecommendVoicesAsync( "warm female voice for a product tutorial", count: 3 ); foreach (var voice in voices) { Console.WriteLine($"{voice.VoiceId} {voice.VoiceName} {voice.Score}"); } ``` Recommendation results contain only `VoiceId`, `VoiceName`, and `Score`. Use `GetVoiceV2Async` or `GetVoicesV2Async` when you need detailed metadata such as supported models, emotions, gender, age, or use cases. ## Configuration Set your API key via environment variable or constructor: ```csharp // Using environment variable (TYPECAST_API_KEY) using var client = new TypecastClient(); // Or pass directly using var client = new TypecastClient("your-api-key-here"); // Or use configuration object var config = new TypecastClientConfig { ApiKey = "your-api-key-here", TimeoutSeconds = 60 // Optional, default: 30 }; using var client = new TypecastClient(config); ``` When requests go through your own proxy, set `ApiHost` to the proxy endpoint and omit `ApiKey`. The SDK will not send the `X-API-KEY` header for empty or missing keys. Requests to the default Typecast host still require an API key. ```csharp Proxy without API key var config = new TypecastClientConfig { ApiHost = "https://your-proxy.example.com" }; using var client = new TypecastClient(config); ``` ## Advanced Usage ### Emotion Control (ssfm-v30) ssfm-v30 offers two emotion control modes: **Preset** and **Smart**. Let the AI infer emotion from context: ```csharp var request = new TTSRequest("Everything is going to be okay.", voiceId, TTSModel.SsfmV30) { Language = LanguageCode.English, Prompt = new SmartPrompt( previousText: "I just got the best news!", nextText: "I can't wait to celebrate!" ) }; var response = await client.TextToSpeechAsync(request); ``` Explicitly set emotion with preset values: ```csharp var request = new TTSRequest("I am so excited to show you these features!", voiceId, TTSModel.SsfmV30) { Language = LanguageCode.English, Prompt = new PresetPrompt( emotionPreset: EmotionPreset.Happy, emotionIntensity: 1.5 // Range: 0.0 to 2.0 ) }; var response = await client.TextToSpeechAsync(request); ``` ### Audio Customization Control loudness, pitch, tempo, and output format: ```csharp var request = new TTSRequest("Customized audio output!", voiceId, TTSModel.SsfmV30) { Language = LanguageCode.English, Output = new Output( targetLufs: -14.0, // Range: -70 to 0 (LUFS) audioPitch: 2, // Range: -12 to +12 semitones audioTempo: 1.2, // Range: 0.5x to 2.0x audioFormat: AudioFormat.Mp3 // Options: Wav, Mp3 ), Seed = 42 // For reproducible results }; var response = await client.TextToSpeechAsync(request); await response.SaveToFileAsync($"output{response.FileExtension}"); Console.WriteLine($"Duration: {response.Duration}s, Format: {response.Format}"); ``` ### Generate audio to a file Use `GenerateToFileAsync` when you want the SDK to synthesize speech and write the audio bytes directly to a local file. The model defaults to `SsfmV30`, and `.mp3` / `.wav` extensions infer the output format when `Output.AudioFormat` is not set. Browse available voice IDs on the [Voices](https://studio.typecast.ai/developers/api/voices) page. ```csharp await client.GenerateToFileAsync("output.mp3", new GenerateToFileRequest { Text = "Hello from Typecast.", VoiceId = "tc_672c5f5ce59fac2a48faeaee" // Find voice IDs at https://studio.typecast.ai/developers/api/voices }); ``` ### Text pauses Use text pause markup when you only need silent gaps inside one composed text segment. Put `<|5s|>`, `<|1s|>`, `<|0.3s|>`, or `<|0.34413s|>` directly in the text. The value is interpreted as seconds and must end with `s`. This keeps the pause expression visible in plain text without adding separate pause calls. ```csharp var audio = await client.ComposeSpeech() .Defaults(new ComposerSettings { VoiceId = "tc_672c5f5ce59fac2a48faeaee", Model = TTSModel.SsfmV30 }) .Say("Hello<|5s|>Nice to meet you<|1s|>Today<|2s|>how does the weather feel?") .GenerateAsync(); ``` ### Multi-speaker composition Use the composer chaining API when one output file needs different voices or per-segment options such as pitch, tempo, prompt, or seed. The composer sends segments to `POST /v1/text-to-speech/compose`, which returns WAV or MP3 directly. Set silence removal explicitly on TTS segments; explicit pauses are preserved. ```csharp using Typecast; using Typecast.Models; using var client = new TypecastClient("YOUR_API_KEY"); var audio = await client.ComposeSpeech() .Defaults(new ComposerSettings { VoiceId = "tc_672c5f5ce59fac2a48faeaee", Model = TTSModel.SsfmV30 }) .Say("Hello there") .Pause(5) .Say("Nice to meet you", new ComposerSettings { VoiceId = "tc_60e5426de8b95f1d3000d7b5", Output = new Output(audioPitch: 2) }) .Say("Today") .Pause(2) .Say("How does the weather feel?") .GenerateAsync(); await audio.SaveToFileAsync("conversation.wav"); ``` ### Voice Discovery (V2 API) List and filter available voices with enhanced metadata: ```csharp // Get all voices var voices = await client.GetVoicesV2Async(); // Filter by criteria var filtered = await client.GetVoicesV2Async(new VoicesV2Filter { Model = TTSModel.SsfmV30, Gender = GenderEnum.Female, Age = AgeEnum.YoungAdult }); // Display voice info foreach (var voice in voices) { Console.WriteLine($"ID: {voice.VoiceId}, Name: {voice.VoiceName}"); Console.WriteLine($"Gender: {voice.Gender}, Age: {voice.Age}"); Console.WriteLine($"Models: {string.Join(", ", voice.Models.Select(m => m.Version))}"); Console.WriteLine($"Use cases: {string.Join(", ", voice.UseCases ?? new List())}"); } ``` ### Multilingual Content The SDK supports 35+ languages with automatic language detection: ```csharp // Auto-detect language (recommended) var request = new TTSRequest("こんにちは。お元気ですか。", voiceId, TTSModel.SsfmV30); var response = await client.TextToSpeechAsync(request); // Or specify language explicitly var koreanRequest = new TTSRequest("안녕하세요. 반갑습니다.", voiceId, TTSModel.SsfmV30) { Language = LanguageCode.Korean // ISO 639-3 language code }; await response.SaveToFileAsync("output.wav"); ``` ## Unity Integration ### Basic Unity Example ```csharp using UnityEngine; using Typecast; using Typecast.Models; public class TypecastTTS : MonoBehaviour { private TypecastClient _client; private AudioSource _audioSource; void Start() { _client = new TypecastClient("your-api-key"); _audioSource = GetComponent(); } public async void SpeakText(string text, string voiceId) { try { var request = new TTSRequest(text, voiceId, TTSModel.SsfmV30) { Language = LanguageCode.English, Output = new Output(audioFormat: AudioFormat.Wav) }; var response = await _client.TextToSpeechAsync(request); // Convert to Unity AudioClip and play var audioClip = CreateAudioClipFromWav(response.AudioData); _audioSource.clip = audioClip; _audioSource.Play(); } catch (TypecastException ex) { Debug.LogError($"TTS Error: {ex.Message}"); } } void OnDestroy() => _client?.Dispose(); } ``` ## Blazor Integration ### Blazor Server Example ```csharp // Program.cs builder.Services.AddSingleton(sp => new TypecastClient(builder.Configuration["Typecast:ApiKey"])); // TTSService.cs public class TTSService { private readonly TypecastClient _client; public TTSService(TypecastClient client) => _client = client; public async Task SynthesizeAsync(string text, string voiceId) { var request = new TTSRequest(text, voiceId, TTSModel.SsfmV30) { Output = new Output(audioFormat: AudioFormat.Mp3) }; var response = await _client.TextToSpeechAsync(request); return response.AudioData; } } ``` ### Blazor Component ```razor @inject TTSService TTSService @inject IJSRuntime JSRuntime @code { private bool IsProcessing { get; set; } private async Task SynthesizeAsync() { IsProcessing = true; try { var audioData = await TTSService.SynthesizeAsync(Text, VoiceId); var base64Audio = Convert.ToBase64String(audioData); await JSRuntime.InvokeVoidAsync("playAudio", $"data:audio/mp3;base64,{base64Audio}"); } finally { IsProcessing = false; } } } ``` ### Streaming Stream audio chunks in real-time for low-latency playback: ```csharp // Stream and extract raw PCM (skip 44-byte WAV header) using var stream = await client.TextToSpeechStreamAsync(request); var buffer = new byte[8192]; bool first = true; while (true) { int bytesRead = await stream.ReadAsync(buffer); if (bytesRead == 0) break; ReadOnlySpan pcm = buffer.AsSpan(0, bytesRead); if (first) { pcm = pcm[44..]; // Skip WAV header first = false; } // pcm is raw 16-bit mono PCM at 32000 Hz // Feed to your audio output (e.g. NAudio) } ``` **WAV streaming format:** 32000 Hz, 16-bit, mono PCM. The first chunk includes a 44-byte WAV header (size = `0xFFFFFFFF`); subsequent chunks are raw PCM only. For MP3: 320 kbps, 44100 Hz, each chunk is independently decodable. ## Timestamp TTS `TextToSpeechWithTimestampsAsync()` wraps `POST /v1/text-to-speech/with-timestamps` and returns the audio together with per-word and per-character alignment data - useful for karaoke highlights, subtitle generation, and lip-sync applications. ### Basic Usage ```csharp using Typecast; using Typecast.Models; using var client = new TypecastClient("YOUR_API_KEY"); var request = new TTSRequestWithTimestamps( text: "Hello. How are you?", voiceId: "tc_60e5426de8b95f1d3000d7b5", model: TTSModel.SsfmV30 ); var result = await client.TextToSpeechWithTimestampsAsync(request); await result.SaveToFileAsync("output.wav"); Console.WriteLine($"Duration: {result.AudioDuration}s"); foreach (var word in result.Words) { Console.WriteLine($" [{word.StartTime:F3}s – {word.EndTime:F3}s] {word.Text}"); } ``` ### Granularity Set `Granularity = Granularity.Word` (default) or `Granularity = Granularity.Char` to control the alignment unit. ```csharp // Character-level alignment - required for Japanese / Chinese var request = new TTSRequestWithTimestamps( text: "Hello. How are you?", voiceId: "tc_60e5426de8b95f1d3000d7b5", model: TTSModel.SsfmV30 ) { Granularity = Granularity.Char }; ``` ### Subtitle Export ```csharp await File.WriteAllTextAsync("output.srt", result.ToSrt(), Encoding.UTF8); await File.WriteAllTextAsync("output.vtt", result.ToVtt(), Encoding.UTF8); ``` **Japanese / Chinese:** Word-level segmentation is not meaningful for languages without whitespace delimiters (jpn, zho). Use `Granularity.Char` for these languages to get character-level alignment. ## Instant Voice Cloning Clone a custom voice from a short audio sample, then pass the returned `uc_` voice ID directly to TTS. ```csharp using Typecast; using Typecast.Models; using var client = new TypecastClient("YOUR_API_KEY"); CustomVoice voice = await client.CloneVoiceAsync( audioFile: "sample.wav", name: "My Voice", model: "ssfm-v30" ); var request = new TTSRequest( text: "Hello from my cloned voice!", voiceId: voice.VoiceId, model: TTSModel.SsfmV30 ); var response = await client.TextToSpeechAsync(request); await response.SaveToFileAsync("output.wav"); await client.DeleteVoiceAsync(voice.VoiceId); ``` Voice cloning audio must be **25 MB or smaller**, the audio duration must be **5-150 seconds**, and the custom voice name must be **1-30 characters**. ## Supported Languages The SDK supports 35+ languages with automatic language detection: | Code | Language | Code | Language | Code | Language | |------|----------|------|----------|------|----------| | `eng` | English | `jpn` | Japanese | `ukr` | Ukrainian | | `kor` | Korean | `ell` | Greek | `ind` | Indonesian | | `spa` | Spanish | `tam` | Tamil | `dan` | Danish | | `deu` | German | `tgl` | Tagalog | `swe` | Swedish | | `fra` | French | `fin` | Finnish | `msa` | Malay | | `ita` | Italian | `zho` | Chinese | `ces` | Czech | | `pol` | Polish | `slk` | Slovak | `por` | Portuguese | | `nld` | Dutch | `ara` | Arabic | `bul` | Bulgarian | | `rus` | Russian | `hrv` | Croatian | `ron` | Romanian | | `ben` | Bengali | `hin` | Hindi | `hun` | Hungarian | | `nan` | Hokkien | `nor` | Norwegian | `pan` | Punjabi | | `tha` | Thai | `tur` | Turkish | `vie` | Vietnamese | | `yue` | Cantonese | | | | | If not specified, the language will be automatically detected from the input text. ## Error Handling The SDK provides specific exception types for handling API errors: ```csharp using Typecast; using Typecast.Exceptions; try { var response = await client.TextToSpeechAsync(request); } catch (UnauthorizedException) { Console.WriteLine("Invalid or missing API key"); } catch (PaymentRequiredException) { Console.WriteLine("Insufficient credits"); } catch (UnprocessableEntityException ex) { Console.WriteLine($"Validation error: {ex.ResponseBody}"); } catch (RateLimitException) { Console.WriteLine("Rate limit exceeded - please retry later"); } catch (TypecastException ex) { Console.WriteLine($"API error ({ex.StatusCode}): {ex.Message}"); } ``` ## Synchronous API For scenarios where async is not preferred, use synchronous methods: ```csharp // Synchronous text-to-speech var response = client.TextToSpeech(request); response.SaveToFile("output.wav"); // Synchronous voice listing var voices = client.GetVoicesV2(); var voice = client.GetVoiceV2("voice_id"); ``` ## Type Reference ```csharp using Typecast; using Typecast.Models; using Typecast.Exceptions; // Main types TypecastClient TypecastClientConfig // Request/Response types TTSRequest TTSResponse VoiceV2Response VoicesV2Filter // Prompt types Prompt PresetPrompt SmartPrompt Output // Enums TTSModel // SsfmV21, SsfmV30 LanguageCode // English, Korean, Japanese, etc. EmotionPreset // Normal, Happy, Sad, Angry, Whisper, ToneUp, ToneDown AudioFormat // Wav, Mp3 GenderEnum // Male, Female AgeEnum // Child, Teenager, YoungAdult, MiddleAge, Elder ``` ## Control silence duration Requires **0.3.13 or later**. `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. Pass these output settings to the corresponding request's `output`. For streaming, use the streaming output type. ```csharp var output = new Typecast.Models.Output { RemoveSilenceMs = 300 }; var streamOutput = new Typecast.Models.OutputStream { RemoveSilenceMs = 300 }; ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Java The official Java library for the [Typecast API](https://typecast.ai). Convert text to lifelike speech using AI-powered voices. Compatible with Java 8 and later versions. Works with Maven, Gradle, and manual installation. Typecast Java SDK Typecast Java SDK Source Code ## Installation Add the following dependency to your `pom.xml`: ```xml com.neosapience typecast-java 1.2.12 ``` Add to your `build.gradle`: ```groovy implementation 'com.neosapience:typecast-java:1.2.12' ``` Clone and install to local Maven repository: ```bash git clone https://github.com/neosapience/typecast-sdk.git cd typecast-sdk/typecast-java mvn clean install -DskipTests ``` Latest registered version: **1.2.12** on Maven Central. Make sure you have **version 1.2.12 or higher** installed. If you have an older version, update your dependency version in `pom.xml` or `build.gradle`. ## Quick Start ```java import com.neosapience.TypecastClient; import com.neosapience.models.*; import java.io.FileOutputStream; public class QuickStart { public static void main(String[] args) throws Exception { // Initialize client TypecastClient client = new TypecastClient("YOUR_API_KEY"); // Convert text to speech TTSRequest request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") // Find voice IDs at https://studio.typecast.ai/developers/api/voices .text("Hello there! I'm your friendly text-to-speech agent.") .model(TTSModel.SSFM_V30) .build(); TTSResponse response = client.textToSpeech(request); // Save audio file try (FileOutputStream fos = new FileOutputStream("output." + response.getFormat())) { fos.write(response.getAudioData()); } System.out.println("Audio saved! Duration: " + response.getDuration() + "s, Format: " + response.getFormat()); // Clean up client.close(); } } ``` ## Features The Typecast Java SDK provides powerful features for text-to-speech conversion: - **Multiple Voice Models**: Support for `ssfm-v30` (latest) and `ssfm-v21` AI voice models - **Multi-language Support**: 35+ languages including English, Korean, Spanish, Japanese, Chinese, and more - **Emotion Control**: Preset emotions (normal, happy, sad, angry, whisper, toneup, tonedown) or smart context-aware inference - **Audio Customization**: Control loudness (LUFS -70 to 0), pitch (-12 to +12 semitones), tempo (0.5x to 2.0x), and format (WAV/MP3) - **Voice Discovery**: V2 Voices API with filtering by model, gender, age, and use cases - **Instant Voice Cloning**: Upload a WAV/MP3 sample and create a custom voice ID - **Timestamp TTS**: Word- and character-level alignment data for subtitles, karaoke, and lip-sync - **Builder Pattern**: Fluent API with builder pattern for easy request construction - **Comprehensive Error Handling**: Specific exception classes for each error type - **Streaming**: Real-time chunked audio delivery for low-latency playback ## Voice Recommendations Use `recommendVoices` when you know the desired style but not the exact `voice_id`. ```java List voices = client.recommendVoices( "warm female voice for a product tutorial", 3 ); for (RecommendedVoice voice : voices) { System.out.println(voice.getVoiceId() + " " + voice.getVoiceName() + " " + voice.getScore()); } ``` Recommendation results contain only `voiceId`, `voiceName`, and `score`. Use `getVoiceV2` or `getVoicesV2` when you need detailed metadata such as supported models, emotions, gender, age, or use cases. ## Configuration Set your API key via environment variable, `.env` file, or constructor: ```java // Using environment variable // export TYPECAST_API_KEY="your-api-key-here" TypecastClient client = new TypecastClient(); // Or pass directly TypecastClient client = new TypecastClient("your-api-key-here"); // Or with custom base URL TypecastClient client = new TypecastClient("your-api-key-here", "https://custom-api.example.com"); ``` When requests go through your own proxy, pass the proxy base URL and omit the API key by passing `null` or an empty string. The SDK will not send the `X-API-KEY` header for empty or missing keys. Requests to the default Typecast host still require an API key. ```java Proxy without API key TypecastClient client = new TypecastClient(null, "https://your-proxy.example.com"); ``` ### Environment File Create a `.env` file in your project root: ```bash TYPECAST_API_KEY=your-api-key-here ``` ## Advanced Usage ### Emotion Control (ssfm-v30) ssfm-v30 offers two emotion control modes: **Preset** and **Smart**. Let the AI infer emotion from context: ```java TTSRequest request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("Everything is going to be okay.") .model(TTSModel.SSFM_V30) .prompt(SmartPrompt.builder() .previousText("I just got the best news!") // Optional context .nextText("I can't wait to celebrate!") // Optional context .build()) .build(); TTSResponse response = client.textToSpeech(request); ``` Explicitly set emotion with preset values: ```java TTSRequest request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("I am so excited to show you these features!") .model(TTSModel.SSFM_V30) .prompt(PresetPrompt.builder() .emotionPreset(EmotionPreset.HAPPY) // normal, happy, sad, angry, whisper, toneup, tonedown .emotionIntensity(1.5) // Range: 0.0 to 2.0 .build()) .build(); TTSResponse response = client.textToSpeech(request); ``` ### Audio Customization Control loudness, pitch, tempo, and output format: ```java TTSRequest request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("Customized audio output!") .model(TTSModel.SSFM_V30) .output(Output.builder() .targetLufs(-14.0) // Range: -70 to 0 (LUFS) .audioPitch(2) // Range: -12 to +12 semitones .audioTempo(1.2) // Range: 0.5x to 2.0x .audioFormat(AudioFormat.MP3) // Options: WAV, MP3 .build()) .seed(42) // Non-negative seed for reproducible results .build(); TTSResponse response = client.textToSpeech(request); try (FileOutputStream fos = new FileOutputStream("output." + response.getFormat())) { fos.write(response.getAudioData()); } System.out.println("Duration: " + response.getDuration() + "s, Format: " + response.getFormat()); ``` ### Generate audio to a file Use `generateToFile` when you want the SDK to synthesize speech and write the audio bytes directly to a local file. The model defaults to `ssfm-v30`, and `.mp3` / `.wav` extensions infer the output format when `Output.audioFormat` is not set. Browse available voice IDs on the [Voices](https://studio.typecast.ai/developers/api/voices) page. ```java client.generateToFile("output.mp3", GenerateToFileRequest.builder() .text("Hello from Typecast.") .voiceId("tc_672c5f5ce59fac2a48faeaee") // Find voice IDs at https://studio.typecast.ai/developers/api/voices .build()); ``` ### Text pauses Use text pause markup when you only need silent gaps inside one composed text segment. Put `<|5s|>`, `<|1s|>`, `<|0.3s|>`, or `<|0.34413s|>` directly in the text. The value is interpreted as seconds and must end with `s`. This keeps the pause expression visible in plain text without adding separate pause calls. ```java TTSResponse audio = client.composeSpeech() .defaults(new ComposerSettings().voiceId("tc_672c5f5ce59fac2a48faeaee").model(TTSModel.SSFM_V30)) .say("Hello<|5s|>Nice to meet you<|1s|>Today<|2s|>how does the weather feel?") .generate(); ``` ### Multi-speaker composition Use the composer chaining API when one output file needs different voices or per-segment options such as pitch, tempo, prompt, or seed. The composer sends segments to `POST /v1/text-to-speech/compose`, which returns WAV or MP3 directly. Set silence removal explicitly on TTS segments; explicit pauses are preserved. ```java TypecastClient client = new TypecastClient("YOUR_API_KEY"); TTSResponse audio = client.composeSpeech() .defaults(new ComposerSettings().setVoiceId("tc_672c5f5ce59fac2a48faeaee").setModel(TTSModel.SSFM_V30)) .say("Hello there") .pause(5) .say("Nice to meet you", new ComposerSettings() .setVoiceId("tc_60e5426de8b95f1d3000d7b5") .setOutput(Output.builder().volume(null).audioPitch(2).build())) .say("Today") .pause(2) .say("How does the weather feel?") .generate(); Files.write(Path.of("conversation.wav"), audio.getAudioData()); ``` ### Voice Discovery (V2 API) List and filter available voices with enhanced metadata: ```java // Get all voices List voices = client.getVoicesV2(); // Filter by criteria VoicesV2Filter filter = VoicesV2Filter.builder() .model(TTSModel.SSFM_V30) .gender(GenderEnum.FEMALE) .age(AgeEnum.YOUNG_ADULT) .build(); List filtered = client.getVoicesV2(filter); // Display voice info for (VoiceV2Response voice : voices) { System.out.println("ID: " + voice.getVoiceId() + ", Name: " + voice.getVoiceName()); System.out.println("Gender: " + voice.getGender() + ", Age: " + voice.getAge()); for (ModelInfo model : voice.getModels()) { System.out.println("Model: " + model.getVersion() + ", Emotions: " + model.getEmotions()); } if (voice.getUseCases() != null) { System.out.println("Use cases: " + String.join(", ", voice.getUseCases())); } } ``` ### Multilingual Content The SDK supports 35+ languages with automatic language detection: ```java // Auto-detect language (recommended) TTSRequest request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("こんにちは。お元気ですか。") .model(TTSModel.SSFM_V30) .build(); TTSResponse response = client.textToSpeech(request); // Or specify language explicitly TTSRequest koreanRequest = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("안녕하세요. 반갑습니다.") .model(TTSModel.SSFM_V30) .language(LanguageCode.KOR) // ISO 639-3 language code .build(); TTSResponse koreanResponse = client.textToSpeech(koreanRequest); try (FileOutputStream fos = new FileOutputStream("output." + response.getFormat())) { fos.write(response.getAudioData()); } ``` ### Streaming Stream audio chunks in real-time for low-latency playback: ```java import javax.sound.sampled.*; // Set up audio playback: 32000 Hz, 16-bit, mono, little-endian AudioFormat format = new AudioFormat(32000, 16, 1, true, false); SourceDataLine line = AudioSystem.getSourceDataLine(format); line.open(format, 8192); line.start(); try (InputStream stream = client.textToSpeechStream(request)) { byte[] buf = new byte[4096]; boolean first = true; int bytesRead; while ((bytesRead = stream.read(buf)) != -1) { int offset = 0; if (first) { offset = 44; // Skip 44-byte WAV header bytesRead -= 44; first = false; } line.write(buf, offset, bytesRead); } } line.drain(); line.close(); client.close(); ``` **WAV streaming format:** 32000 Hz, 16-bit, mono PCM. The first chunk includes a 44-byte WAV header (size = `0xFFFFFFFF`); subsequent chunks are raw PCM only. For MP3: 320 kbps, 44100 Hz, each chunk is independently decodable. Use `com.neosapience.models.OutputStream` to avoid collision with `java.io.OutputStream`. ## Timestamp TTS `textToSpeechWithTimestamps()` wraps `POST /v1/text-to-speech/with-timestamps` and returns the audio together with per-word and per-character alignment data - useful for karaoke highlights, subtitle generation, and lip-sync applications. ### Basic Usage ```java import com.neosapience.TypecastClient; import com.neosapience.models.*; import java.io.FileOutputStream; import java.nio.file.Files; import java.nio.file.Paths; TypecastClient client = new TypecastClient("YOUR_API_KEY"); TTSRequestWithTimestamps request = TTSRequestWithTimestamps.builder() .voiceId("tc_60e5426de8b95f1d3000d7b5") .text("Hello. How are you?") .model(TTSModel.SSFM_V30) .build(); TTSWithTimestampsResponse result = client.textToSpeechWithTimestamps(request); Files.write(Paths.get("output.wav"), result.getAudioBytes()); System.out.printf("Duration: %.3fs%n", result.getAudioDuration()); for (WordAlignment word : result.getWords()) { System.out.printf(" [%.3fs – %.3fs] %s%n", word.getStartTime(), word.getEndTime(), word.getText()); } client.close(); ``` ### Granularity Pass `.granularity(Granularity.WORD)` (default) or `.granularity(Granularity.CHAR)` to control the alignment unit. ```java TTSRequestWithTimestamps request = TTSRequestWithTimestamps.builder() .voiceId("tc_60e5426de8b95f1d3000d7b5") .text("Hello. How are you?") .model(TTSModel.SSFM_V30) .granularity(Granularity.CHAR) // required for Japanese / Chinese .build(); ``` ### Subtitle Export ```java // Export SRT captions String srt = result.toSrt(); Files.writeString(Paths.get("output.srt"), srt); // Export WebVTT captions String vtt = result.toVtt(); Files.writeString(Paths.get("output.vtt"), vtt); ``` **Japanese / Chinese:** Word-level segmentation is not meaningful for languages without whitespace delimiters (jpn, zho). Use `Granularity.CHAR` for these languages to get character-level alignment. ## Instant Voice Cloning Clone a custom voice from a short audio sample, then pass the returned `uc_` voice ID directly to TTS. ```java import com.neosapience.TypecastClient; import com.neosapience.models.*; import java.io.File; TypecastClient client = new TypecastClient("YOUR_API_KEY"); CustomVoice voice = client.cloneVoice( new File("sample.wav"), "My Voice", "ssfm-v30" ); TTSRequest request = TTSRequest.builder() .voiceId(voice.getVoiceId()) .text("Hello from my cloned voice!") .model(TTSModel.SSFM_V30) .build(); TTSResponse response = client.textToSpeech(request); client.deleteVoice(voice.getVoiceId()); client.close(); ``` Voice cloning audio must be **25 MB or smaller**, the audio duration must be **5-150 seconds**, and the custom voice name must be **1-30 characters**. ## Supported Languages The SDK supports 35+ languages with automatic language detection: | Code | Language | Code | Language | Code | Language | |------|----------|------|----------|------|----------| | `ENG` | English | `JPN` | Japanese | `UKR` | Ukrainian | | `KOR` | Korean | `ELL` | Greek | `IND` | Indonesian | | `SPA` | Spanish | `TAM` | Tamil | `DAN` | Danish | | `DEU` | German | `TGL` | Tagalog | `SWE` | Swedish | | `FRA` | French | `FIN` | Finnish | `MSA` | Malay | | `ITA` | Italian | `ZHO` | Chinese | `CES` | Czech | | `POL` | Polish | `SLK` | Slovak | `POR` | Portuguese | | `NLD` | Dutch | `ARA` | Arabic | `BUL` | Bulgarian | | `RUS` | Russian | `HRV` | Croatian | `RON` | Romanian | | `BEN` | Bengali | `HIN` | Hindi | `HUN` | Hungarian | | `NAN` | Hokkien | `NOR` | Norwegian | `PAN` | Punjabi | | `THA` | Thai | `TUR` | Turkish | `VIE` | Vietnamese | | `YUE` | Cantonese | | | | | If not specified, the language will be automatically detected from the input text. ## Error Handling The SDK provides specific exception classes for handling API errors: ```java import com.neosapience.TypecastClient; import com.neosapience.exceptions.*; try { TTSResponse response = client.textToSpeech(request); } catch (UnauthorizedException e) { // 401: Invalid API key System.err.println("Invalid API key: " + e.getMessage()); } catch (PaymentRequiredException e) { // 402: Insufficient credits System.err.println("Insufficient credits: " + e.getMessage()); } catch (ForbiddenException e) { // 403: Access denied System.err.println("Access denied: " + e.getMessage()); } catch (NotFoundException e) { // 404: Resource not found System.err.println("Voice not found: " + e.getMessage()); } catch (UnprocessableEntityException e) { // 422: Validation error System.err.println("Validation error: " + e.getMessage()); } catch (RateLimitException e) { // 429: Rate limit exceeded System.err.println("Rate limit exceeded - please retry later"); } catch (InternalServerException e) { // 500: Server error System.err.println("Server error: " + e.getMessage()); } catch (TypecastException e) { // Generic error System.err.println("API error (" + e.getStatusCode() + "): " + e.getMessage()); } ``` ### Exception Hierarchy | Exception | Status Code | Description | |-----------|-------------|-------------| | `BadRequestException` | 400 | Invalid request parameters | | `UnauthorizedException` | 401 | Invalid or missing API key | | `PaymentRequiredException` | 402 | Insufficient credits | | `ForbiddenException` | 403 | Access denied | | `NotFoundException` | 404 | Resource not found | | `UnprocessableEntityException` | 422 | Validation error | | `RateLimitException` | 429 | Rate limit exceeded | | `InternalServerException` | 500 | Server error | | `TypecastException` | * | Base exception class | ## Eclipse IDE Setup 1. Open Eclipse 2. Go to `File` → `Import...` 3. Select `Maven` → `Existing Maven Projects` 4. Browse to the `typecast-java` directory 5. Click `Finish` Add to your project's `pom.xml`: ```xml com.neosapience typecast-java 1.2.12 ``` Right-click on your project → `Maven` → `Update Project...` ## IntelliJ IDEA Setup 1. Open IntelliJ IDEA 2. Go to `File` → `Open...` 3. Select the `typecast-java` directory 4. Select "Open as Project" IntelliJ will automatically detect the `pom.xml` and import dependencies. Or add to your project's `pom.xml`: ```xml com.neosapience typecast-java 1.2.12 ``` Click the Maven refresh button or right-click `pom.xml` → `Maven` → `Reload Project` ## API Reference ### TypecastClient Methods | Method | Description | |--------|-------------| | `textToSpeech(TTSRequest)` | Convert text to speech audio | | `generateToFile(String, GenerateToFileRequest)` | Generate speech and save it directly to a local file | | `cloneVoice(byte[], filename, name, model)` | Create a custom voice via instant cloning | | `cloneVoice(File, name, model)` | Create a custom voice from a local audio file | | `deleteVoice(String voiceId)` | Delete a custom cloned voice | | `getVoicesV2()` | Get all available voices | | `getVoicesV2(VoicesV2Filter)` | Get filtered voices | | `getVoiceV2(String voiceId)` | Get a specific voice by ID | | `close()` | Release resources | ### TTSRequest Fields | Field | Type | Required | Description | |-------|------|----------|-------------| | `voiceId` | `String` | ✓ | Voice ID (format: `tc_*` or `uc_*`) | | `text` | `String` | ✓ | Text to synthesize (max 5000 chars) | | `model` | `TTSModel` | ✓ | TTS model (`SSFM_V21` or `SSFM_V30`) | | `language` | `LanguageCode` | | ISO 639-3 code (auto-detected if omitted) | | `prompt` | `Prompt` / `PresetPrompt` / `SmartPrompt` | | Emotion settings | | `output` | `Output` | | Audio output settings | | `seed` | `Integer` | | Non-negative integer seed for reproducibility (≥ 0) | ### TTSResponse Fields | Field | Type | Description | |-------|------|-------------| | `audioData` | `byte[]` | Generated audio data | | `duration` | `double` | Audio duration in seconds | | `format` | `String` | Audio format (`wav` or `mp3`) | ## Control silence duration Requires **1.2.12 or later**. `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. Pass these output settings to the corresponding request's `output`. For streaming, use the streaming output type. ```java Output output = Output.builder().removeSilenceMs(300).build(); OutputStream streamOutput = OutputStream.builder().removeSilenceMs(300).build(); ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Kotlin The official Kotlin library for the [Typecast API](https://typecast.ai). Convert text to lifelike speech using AI-powered voices. Compatible with Kotlin 1.9+ and JDK 17 or later. Works with Gradle (Kotlin DSL or Groovy) and Maven. Typecast Kotlin SDK Typecast Kotlin SDK Source Code ## Installation Add the following dependency to your `build.gradle.kts`: ```kotlin dependencies { implementation("com.neosapience:typecast-kotlin:1.2.13") } ``` Add to your `build.gradle`: ```groovy implementation 'com.neosapience:typecast-kotlin:1.2.13' ``` Add the following dependency to your `pom.xml`: ```xml com.neosapience typecast-kotlin 1.2.13 ``` Latest registered version: **1.2.13** on Maven Central. Make sure you have **version 1.2.13 or higher** installed. If you have an older version, update your dependency version. ## Quick Start ```kotlin import com.neosapience.TypecastClient import com.neosapience.models.* import java.io.File fun main() { // Initialize client val client = TypecastClient.create("YOUR_API_KEY") // Convert text to speech val request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("Hello there! I'm your friendly text-to-speech agent.") .model(TTSModel.SSFM_V30) .build() val response = client.textToSpeech(request) // Save audio file File("output.${response.format}").writeBytes(response.audioData) println("Audio saved! Duration: ${response.duration}s, Format: ${response.format}") // Clean up client.close() } ``` ## Features The Typecast Kotlin SDK provides powerful features for text-to-speech conversion: - **Multiple Voice Models**: Support for `ssfm-v30` (latest) and `ssfm-v21` AI voice models - **Multi-language Support**: 35+ languages including English, Korean, Spanish, Japanese, Chinese, and more - **Emotion Control**: Preset emotions (normal, happy, sad, angry, whisper, toneup, tonedown) or smart context-aware inference - **Audio Customization**: Control loudness (LUFS -70 to 0), pitch (-12 to +12 semitones), tempo (0.5x to 2.0x), and format (WAV/MP3) - **Voice Discovery**: V2 Voices API with filtering by model, gender, age, and use cases - **Instant Voice Cloning**: Upload a WAV/MP3 sample and create a custom voice ID - **Timestamp TTS**: Word- and character-level alignment data for subtitles, karaoke, and lip-sync - **Idiomatic Kotlin**: Builder pattern with Kotlin-friendly syntax using data classes - **Comprehensive Error Handling**: Specific exception classes for each error type - **Streaming**: Real-time chunked audio delivery for low-latency playback ## Voice Recommendations Use `recommendVoices` when you know the desired style but not the exact `voice_id`. ```kotlin val voices = client.recommendVoices( "warm female voice for a product tutorial", count = 3, ) voices.forEach { voice -> println("${voice.voiceId} ${voice.voiceName} ${voice.score}") } ``` Recommendation results contain only `voiceId`, `voiceName`, and `score`. Use `getVoiceV2` or `getVoicesV2` when you need detailed metadata such as supported models, emotions, gender, age, or use cases. ## Configuration Set your API key via environment variable, `.env` file, or builder: ```kotlin // Using environment variable // export TYPECAST_API_KEY="your-api-key-here" val client = TypecastClient.create() // Or pass directly val client = TypecastClient.create("your-api-key-here") // Or use builder for custom configuration val client = TypecastClient.builder() .apiKey("your-api-key-here") .baseUrl("https://custom-api.example.com") .build() ``` When requests go through your own proxy, set `baseUrl` to the proxy endpoint and omit `apiKey`. The SDK will not send the `X-API-KEY` header for empty or missing keys. Requests to the default Typecast host still require an API key. ```kotlin Proxy without API key val client = TypecastClient.builder() .baseUrl("https://your-proxy.example.com") .build() ``` ### Environment File Create a `.env` file in your project root: ```bash TYPECAST_API_KEY=your-api-key-here ``` ## Advanced Usage ### Emotion Control (ssfm-v30) ssfm-v30 offers two emotion control modes: **Preset** and **Smart**. Let the AI infer emotion from context: ```kotlin val request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("Everything is going to be okay.") .model(TTSModel.SSFM_V30) .prompt(SmartPrompt.builder() .previousText("I just got the best news!") // Optional context .nextText("I can't wait to celebrate!") // Optional context .build()) .build() val response = client.textToSpeech(request) ``` Explicitly set emotion with preset values: ```kotlin val request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("I am so excited to show you these features!") .model(TTSModel.SSFM_V30) .prompt(PresetPrompt.builder() .emotionPreset(EmotionPreset.HAPPY) // normal, happy, sad, angry, whisper, toneup, tonedown .emotionIntensity(1.5) // Range: 0.0 to 2.0 .build()) .build() val response = client.textToSpeech(request) ``` ### Audio Customization Control loudness, pitch, tempo, and output format: ```kotlin val request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("Customized audio output!") .model(TTSModel.SSFM_V30) .output(Output.builder() .targetLufs(-14.0) // Range: -70 to 0 (LUFS) .audioPitch(2) // Range: -12 to +12 semitones .audioTempo(1.2) // Range: 0.5x to 2.0x .audioFormat(AudioFormat.MP3) // Options: WAV, MP3 .build()) .seed(42) // Unsigned seed for reproducible results .build() val response = client.textToSpeech(request) File("output.${response.format}").writeBytes(response.audioData) println("Duration: ${response.duration}s, Format: ${response.format}") ``` ### Generate audio to a file Use `generateToFile` when you want the SDK to synthesize speech and write the audio bytes directly to a local file. The model defaults to `ssfm-v30`, and `.mp3` / `.wav` extensions infer the output format when `output.audioFormat` is not set. Browse available voice IDs on the [Voices](https://studio.typecast.ai/developers/api/voices) page. ```kotlin client.generateToFile( "output.mp3", GenerateToFileRequest( text = "Hello from Typecast.", voiceId = "tc_672c5f5ce59fac2a48faeaee", // Find voice IDs at https://studio.typecast.ai/developers/api/voices ) ) ``` ### Text pauses Use text pause markup when you only need silent gaps inside one composed text segment. Put `<|5s|>`, `<|1s|>`, `<|0.3s|>`, or `<|0.34413s|>` directly in the text. The value is interpreted as seconds and must end with `s`. This keeps the pause expression visible in plain text without adding separate pause calls. ```kotlin val audio = client.composeSpeech() .defaults(ComposerSettings(voiceId = "tc_672c5f5ce59fac2a48faeaee", model = TTSModel.SSFM_V30)) .say("Hello<|5s|>Nice to meet you<|1s|>Today<|2s|>how does the weather feel?") .generate() ``` ### Multi-speaker composition Use the composer chaining API when one output file needs different voices or per-segment options such as pitch, tempo, prompt, or seed. The composer sends segments to `POST /v1/text-to-speech/compose`, which returns WAV or MP3 directly. Set silence removal explicitly on TTS segments; explicit pauses are preserved. ```kotlin val client = TypecastClient.create("YOUR_API_KEY") val audio = client.composeSpeech() .defaults(ComposerSettings(voiceId = "tc_672c5f5ce59fac2a48faeaee", model = TTSModel.SSFM_V30)) .say("Hello there") .pause(5.0) .say("Nice to meet you", ComposerSettings(voiceId = "tc_60e5426de8b95f1d3000d7b5", output = Output(audioPitch = 2))) .say("Today") .pause(2.0) .say("How does the weather feel?") .generate() File("conversation.wav").writeBytes(audio.audioData) ``` ### Voice Discovery (V2 API) List and filter available voices with enhanced metadata: ```kotlin // Get all voices val voices = client.getVoicesV2() // Filter by criteria val filter = VoicesV2Filter.builder() .model(TTSModel.SSFM_V30) .gender(GenderEnum.FEMALE) .age(AgeEnum.YOUNG_ADULT) .build() val filtered = client.getVoicesV2(filter) // Display voice info voices.forEach { voice -> println("ID: ${voice.voiceId}, Name: ${voice.voiceName}") println("Gender: ${voice.gender}, Age: ${voice.age}") voice.models.forEach { model -> println("Model: ${model.version}, Emotions: ${model.emotions}") } voice.useCases?.let { useCases -> println("Use cases: ${useCases.joinToString(", ")}") } } ``` ### Multilingual Content The SDK supports 35+ languages with automatic language detection: ```kotlin // Auto-detect language (recommended) val request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("こんにちは。お元気ですか。") .model(TTSModel.SSFM_V30) .build() val response = client.textToSpeech(request) // Or specify language explicitly val koreanRequest = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("안녕하세요. 반갑습니다.") .model(TTSModel.SSFM_V30) .language(LanguageCode.KOR) // ISO 639-3 language code .build() val koreanResponse = client.textToSpeech(koreanRequest) File("output.${response.format}").writeBytes(response.audioData) ``` ### Streaming Stream audio chunks in real-time for low-latency playback: ```kotlin import javax.sound.sampled.* // Set up audio playback: 32000 Hz, 16-bit, mono, little-endian val format = AudioFormat(32000f, 16, 1, true, false) val line = AudioSystem.getSourceDataLine(format).apply { open(format, 8192) start() } val stream = client.textToSpeechStream(request) val buf = ByteArray(4096) var first = true while (true) { val bytesRead = stream.read(buf) if (bytesRead == -1) break var offset = 0 var len = bytesRead if (first) { offset = 44 // Skip 44-byte WAV header len -= 44 first = false } line.write(buf, offset, len) } line.drain() line.close() stream.close() client.close() ``` **WAV streaming format:** 32000 Hz, 16-bit, mono PCM. The first chunk includes a 44-byte WAV header (size = `0xFFFFFFFF`); subsequent chunks are raw PCM only. For MP3: 320 kbps, 44100 Hz, each chunk is independently decodable. ## Timestamp TTS `textToSpeechWithTimestamps()` wraps `POST /v1/text-to-speech/with-timestamps` and returns the audio together with per-word and per-character alignment data - useful for karaoke highlights, subtitle generation, and lip-sync applications. ### Basic Usage ```kotlin import com.neosapience.TypecastClient import com.neosapience.models.* import java.io.File val client = TypecastClient.create("YOUR_API_KEY") val request = TTSRequestWithTimestamps.builder() .voiceId("tc_60e5426de8b95f1d3000d7b5") .text("Hello. How are you?") .model(TTSModel.SSFM_V30) .build() val result = client.textToSpeechWithTimestamps(request) File("output.wav").writeBytes(result.audioBytes) println("Duration: ${result.audioDuration}s") result.words.forEach { w -> println(" [${w.startTime}s – ${w.endTime}s] ${w.text}") } client.close() ``` ### Granularity Pass `.granularity(Granularity.WORD)` (default) or `.granularity(Granularity.CHAR)` to control the alignment unit. ```kotlin val request = TTSRequestWithTimestamps.builder() .voiceId("tc_60e5426de8b95f1d3000d7b5") .text("Hello. How are you?") .model(TTSModel.SSFM_V30) .granularity(Granularity.CHAR) // required for Japanese / Chinese .build() ``` ### Subtitle Export ```kotlin File("output.srt").writeText(result.toSrt()) File("output.vtt").writeText(result.toVtt()) ``` **Japanese / Chinese:** Word-level segmentation is not meaningful for languages without whitespace delimiters (jpn, zho). Use `Granularity.CHAR` for these languages to get character-level alignment. ## Instant Voice Cloning Clone a custom voice from a short audio sample, then pass the returned `uc_` voice ID directly to TTS. ```kotlin import com.neosapience.TypecastClient import com.neosapience.models.* import java.io.File val client = TypecastClient.create("YOUR_API_KEY") val voice = client.cloneVoice( File("sample.wav"), "My Voice", "ssfm-v30", ) val request = TTSRequest.builder() .voiceId(voice.voiceId) .text("Hello from my cloned voice!") .model(TTSModel.SSFM_V30) .build() val response = client.textToSpeech(request) client.deleteVoice(voice.voiceId) client.close() ``` Voice cloning audio must be **25 MB or smaller**, the audio duration must be **5-150 seconds**, and the custom voice name must be **1-30 characters**. ## Supported Languages The SDK supports 35+ languages with automatic language detection: | Code | Language | Code | Language | Code | Language | |------|----------|------|----------|------|----------| | `ENG` | English | `JPN` | Japanese | `UKR` | Ukrainian | | `KOR` | Korean | `ELL` | Greek | `IND` | Indonesian | | `SPA` | Spanish | `TAM` | Tamil | `DAN` | Danish | | `DEU` | German | `TGL` | Tagalog | `SWE` | Swedish | | `FRA` | French | `FIN` | Finnish | `MSA` | Malay | | `ITA` | Italian | `ZHO` | Chinese | `CES` | Czech | | `POL` | Polish | `SLK` | Slovak | `POR` | Portuguese | | `NLD` | Dutch | `ARA` | Arabic | `BUL` | Bulgarian | | `RUS` | Russian | `HRV` | Croatian | `RON` | Romanian | | `BEN` | Bengali | `HIN` | Hindi | `HUN` | Hungarian | | `NAN` | Hokkien | `NOR` | Norwegian | `PAN` | Punjabi | | `THA` | Thai | `TUR` | Turkish | `VIE` | Vietnamese | | `YUE` | Cantonese | | | | | If not specified, the language will be automatically detected from the input text. ## Error Handling The SDK provides specific exception classes for handling API errors: ```kotlin import com.neosapience.TypecastClient import com.neosapience.exceptions.* try { val response = client.textToSpeech(request) } catch (e: UnauthorizedException) { // 401: Invalid API key println("Invalid API key: ${e.message}") } catch (e: PaymentRequiredException) { // 402: Insufficient credits println("Insufficient credits: ${e.message}") } catch (e: ForbiddenException) { // 403: Access denied println("Access denied: ${e.message}") } catch (e: NotFoundException) { // 404: Resource not found println("Voice not found: ${e.message}") } catch (e: UnprocessableEntityException) { // 422: Validation error println("Validation error: ${e.message}") } catch (e: RateLimitException) { // 429: Rate limit exceeded println("Rate limit exceeded - please retry later") } catch (e: InternalServerException) { // 500: Server error println("Server error: ${e.message}") } catch (e: TypecastException) { // Generic error println("API error (${e.statusCode}): ${e.message}") } ``` ### Exception Hierarchy | Exception | Status Code | Description | |-----------|-------------|-------------| | `BadRequestException` | 400 | Invalid request parameters | | `UnauthorizedException` | 401 | Invalid or missing API key | | `PaymentRequiredException` | 402 | Insufficient credits | | `ForbiddenException` | 403 | Access denied | | `NotFoundException` | 404 | Resource not found | | `UnprocessableEntityException` | 422 | Validation error | | `RateLimitException` | 429 | Rate limit exceeded | | `InternalServerException` | 500 | Server error | | `TypecastException` | * | Base exception class | ## IntelliJ IDEA Setup 1. Open IntelliJ IDEA 2. Go to `File` → `New` → `Project...` 3. Select "Kotlin" and "Gradle (Kotlin)" 4. Set JDK to 17 or higher Add to your `build.gradle.kts`: ```kotlin dependencies { implementation("com.neosapience:typecast-kotlin:1.2.13") } ``` Click the Gradle sync button or right-click `build.gradle.kts` → `Reload Gradle Project` ## Android Setup Add to your app's `build.gradle.kts`: ```kotlin dependencies { implementation("com.neosapience:typecast-kotlin:1.2.13") } ``` Add to your `AndroidManifest.xml`: ```xml ``` Make API calls from a coroutine or background thread: ```kotlin lifecycleScope.launch(Dispatchers.IO) { val client = TypecastClient.create("YOUR_API_KEY") val response = client.textToSpeech(request) // Handle response } ``` ## API Reference ### TypecastClient Methods | Method | Description | |--------|-------------| | `textToSpeech(TTSRequest)` | Convert text to speech audio | | `generateToFile(path, GenerateToFileRequest)` | Generate speech and save it directly to a local file | | `cloneVoice(ByteArray, filename, name, model)` | Create a custom voice via instant cloning | | `cloneVoice(File, name, model)` | Create a custom voice from a local audio file | | `deleteVoice(voiceId: String)` | Delete a custom cloned voice | | `getVoicesV2()` | Get all available voices | | `getVoicesV2(VoicesV2Filter)` | Get filtered voices | | `getVoiceV2(voiceId: String)` | Get a specific voice by ID | | `close()` | Release resources | ### TTSRequest Fields | Field | Type | Required | Description | |-------|------|----------|-------------| | `voiceId` | `String` | ✓ | Voice ID (format: `tc_*` or `uc_*`) | | `text` | `String` | ✓ | Text to synthesize (max 2000 chars) | | `model` | `TTSModel` | ✓ | TTS model (`SSFM_V21` or `SSFM_V30`) | | `language` | `LanguageCode` | | ISO 639-3 code (auto-detected if omitted) | | `prompt` | `Prompt` / `PresetPrompt` / `SmartPrompt` | | Emotion settings | | `output` | `Output` | | Audio output settings | | `seed` | `UInt32` | | Unsigned integer seed for reproducibility (≥ 0) | ### TTSResponse Fields | Field | Type | Description | |-------|------|-------------| | `audioData` | `ByteArray` | Generated audio data | | `duration` | `Double` | Audio duration in seconds | | `format` | `String` | Audio format (`wav` or `mp3`) | ## Control silence duration Requires **1.2.13 or later**. `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. Pass these output settings to the corresponding request's `output`. For streaming, use the streaming output type. ```kotlin val output = Output(removeSilenceMs = 300) val streamOutput = OutputStream(removeSilenceMs = 300) ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # C/C++ The official C/C++ library for the [Typecast API](https://typecast.ai). Convert text to lifelike speech using AI-powered voices. Compatible with C11 and later versions. Works with CMake, manual compilation, and supports cross-platform development including Windows, Linux, macOS, and embedded systems. Typecast C SDK Source Code Typecast API Documentation ## Requirements - CMake 3.14+ - libcurl (with SSL support) - C11 compatible compiler ```bash sudo apt-get install build-essential cmake libcurl4-openssl-dev ``` ```bash # libcurl is included with Xcode xcode-select --install brew install cmake ``` ```powershell vcpkg install curl:x64-windows ``` ## Installation Clone the repository and build with CMake: ```bash git clone https://github.com/neosapience/typecast-sdk.git cd typecast-sdk/typecast-c mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release cmake --build . ``` Compile directly with GCC or Clang: ```bash git clone https://github.com/neosapience/typecast-sdk.git cd typecast-sdk/typecast-c # Compile source files gcc -c src/typecast.c src/cJSON.c -I include -I src -O2 # Create static library ar rcs libtypecast.a typecast.o cJSON.o # Or compile your application directly gcc -o myapp myapp.c src/typecast.c src/cJSON.c \ -I include -I src -lcurl -O2 ``` Add to your `CMakeLists.txt`: ```cmake include(FetchContent) FetchContent_Declare( typecast GIT_REPOSITORY https://github.com/neosapience/typecast-sdk.git SOURCE_SUBDIR typecast-c GIT_TAG v1.2.13 ) FetchContent_MakeAvailable(typecast) target_link_libraries(your_target PRIVATE typecast) ``` Latest registered version: **v1.2.13** in the SDK Git tags. ### Build Options | Option | Default | Description | |--------|---------|-------------| | `TYPECAST_BUILD_SHARED` | ON | Build shared library (.dll/.so/.dylib) | | `TYPECAST_BUILD_STATIC` | OFF | Build static library | | `TYPECAST_BUILD_EXAMPLES` | ON | Build example programs | | `TYPECAST_BUILD_TESTS` | ON | Build test programs | ## Quick Start ```c #include "typecast.h" #include int main() { // Initialize client TypecastClient* client = typecast_client_create("YOUR_API_KEY"); if (!client) return 1; // Convert text to speech TypecastTTSRequest request = {0}; request.text = "Hello there! I'm your friendly text-to-speech agent."; request.voice_id = "tc_672c5f5ce59fac2a48faeaee"; /* Find voice IDs at https://studio.typecast.ai/developers/api/voices */ request.model = TYPECAST_MODEL_SSFM_V30; request.language = "eng"; TypecastTTSResponse* response = typecast_text_to_speech(client, &request); if (response) { // Save audio file FILE* fp = fopen("output.wav", "wb"); fwrite(response->audio_data, 1, response->audio_size, fp); fclose(fp); printf("Audio saved! Duration: %.2fs, Size: %zu bytes\n", response->duration, response->audio_size); typecast_tts_response_free(response); } // Clean up typecast_client_destroy(client); return 0; } ``` ## Features The Typecast C/C++ SDK provides powerful features for text-to-speech conversion: - **C and C++ Support**: Pure C API with optional C++ wrapper for convenience - **Multiple Voice Models**: Support for `ssfm-v30` (latest) and `ssfm-v21` AI voice models - **Multi-language Support**: 35+ languages including English, Korean, Spanish, Japanese, Chinese, and more - **Emotion Control**: Preset emotions (normal, happy, sad, angry, whisper, toneup, tonedown) or smart context-aware inference - **Audio Customization**: Control loudness (LUFS -70 to 0), pitch (-12 to +12 semitones), tempo (0.5x to 2.0x), and format (WAV/MP3) - **Voice Discovery**: V2 Voices API with filtering by model, gender, age, and use cases - **Instant Voice Cloning**: Upload a WAV/MP3 sample and create a custom voice ID - **Cross-Platform**: Windows, Linux, macOS, ARM (32/64-bit) support - **Embedded Ready**: Optimized for minimal footprint, cross-compilation support - **Timestamp TTS**: Word- and character-level alignment data for subtitles, karaoke, and lip-sync - **Unreal Engine Ready**: Designed for easy integration with game engines - **Streaming**: Real-time chunked audio delivery for low-latency playback ## Voice Recommendations Use `typecast_recommend_voices` when you know the desired style but not the exact `voice_id`. ```c TypecastRecommendedVoicesResponse* voices = typecast_recommend_voices( client, "warm female voice for a product tutorial", 3 ); if (voices) { for (int i = 0; i < voices->count; i++) { printf("%s %s %.3f\n", voices->voices[i].voice_id, voices->voices[i].voice_name, voices->voices[i].score); } typecast_recommended_voices_response_free(voices); } ``` Recommendation results contain only `voice_id`, `voice_name`, and `score`. Use `typecast_get_voice` or `typecast_get_voices` when you need detailed metadata such as supported models, emotions, gender, age, or use cases. ## Configuration Set your API key via environment variable or constructor: ```c #include // Using environment variable // export TYPECAST_API_KEY="your-api-key-here" const char* api_key = getenv("TYPECAST_API_KEY"); TypecastClient* client = typecast_client_create(api_key); // Or pass directly TypecastClient* client = typecast_client_create("your-api-key-here"); // Or with custom base URL TypecastClient* client = typecast_client_create_with_host( "your-api-key-here", "https://custom-api.example.com" ); ``` When requests go through your own proxy, pass the proxy host and omit the API key by passing `NULL` or an empty string. The SDK will not send the `X-API-KEY` header for empty or missing keys. Requests to the default Typecast host still require an API key. ```c Proxy without API key TypecastClient* client = typecast_client_create_with_host( NULL, "https://your-proxy.example.com" ); ``` ## Advanced Usage ### Emotion Control (ssfm-v30) ssfm-v30 offers two emotion control modes: **Preset** and **Smart**. Let the AI infer emotion from context: ```c TypecastTTSRequest request = {0}; request.text = "Everything is going to be okay."; request.voice_id = "tc_672c5f5ce59fac2a48faeaee"; request.model = TYPECAST_MODEL_SSFM_V30; request.language = "eng"; // Smart emotion with context TypecastPrompt prompt = {0}; prompt.emotion_type = TYPECAST_EMOTION_TYPE_SMART; prompt.previous_text = "I just got the best news!"; // Optional context prompt.next_text = "I can't wait to celebrate!"; // Optional context request.prompt = &prompt; TypecastTTSResponse* response = typecast_text_to_speech(client, &request); ``` Explicitly set emotion with preset values: ```c TypecastTTSRequest request = {0}; request.text = "I am so excited to show you these features!"; request.voice_id = "tc_672c5f5ce59fac2a48faeaee"; request.model = TYPECAST_MODEL_SSFM_V30; request.language = "eng"; // Preset emotion TypecastPrompt prompt = TYPECAST_PROMPT_DEFAULT(); prompt.emotion_type = TYPECAST_EMOTION_TYPE_PRESET; prompt.emotion_preset = TYPECAST_EMOTION_HAPPY; // normal, happy, sad, angry, whisper, toneup, tonedown prompt.emotion_intensity = 1.5f; // Range: 0.0 to 2.0 request.prompt = &prompt; TypecastTTSResponse* response = typecast_text_to_speech(client, &request); ``` ### Audio Customization Control loudness, pitch, tempo, and output format: ```c TypecastTTSRequest request = {0}; request.text = "Customized audio output!"; request.voice_id = "tc_672c5f5ce59fac2a48faeaee"; request.model = TYPECAST_MODEL_SSFM_V30; request.language = "eng"; // Configure output settings TypecastOutput output = TYPECAST_OUTPUT_DEFAULT(); output.use_target_lufs = 1; output.target_lufs = -14.0f; // Range: -70 to 0 (LUFS) output.audio_pitch = 2; // Range: -12 to +12 semitones output.audio_tempo = 1.2f; // Range: 0.5x to 2.0x output.audio_format = TYPECAST_AUDIO_FORMAT_MP3; // Options: WAV, MP3 request.output = &output; request.seed = 42; // Unsigned seed for reproducible results TypecastTTSResponse* response = typecast_text_to_speech(client, &request); if (response) { const char* ext = (response->format == TYPECAST_AUDIO_FORMAT_MP3) ? "mp3" : "wav"; char filename[64]; snprintf(filename, sizeof(filename), "output.%s", ext); FILE* fp = fopen(filename, "wb"); fwrite(response->audio_data, 1, response->audio_size, fp); fclose(fp); printf("Duration: %.2fs, Format: %s\n", response->duration, ext); typecast_tts_response_free(response); } ``` ### Generate audio to a file Use `typecast_generate_to_file` when you want the SDK to synthesize speech and write the audio bytes directly to a local file. The model defaults to `ssfm-v30`, and `.mp3` / `.wav` extensions infer the output format when no output format is set. Browse available voice IDs on the [Voices](https://studio.typecast.ai/developers/api/voices) page. ```c TypecastGenerateToFileRequest request = {0}; request.text = "Hello from Typecast."; request.voice_id = "tc_672c5f5ce59fac2a48faeaee"; /* Find voice IDs at https://studio.typecast.ai/developers/api/voices */ TypecastErrorCode code = typecast_generate_to_file(client, "output.mp3", &request); if (code != TYPECAST_SUCCESS) { fprintf(stderr, "Failed to generate audio: %s\n", typecast_error_string(code)); } ``` For C++ wrapper users: ```cpp typecast::GenerateToFileRequest request; request.text = "Hello from Typecast."; request.voiceId = "tc_672c5f5ce59fac2a48faeaee"; // Find voice IDs at https://studio.typecast.ai/developers/api/voices auto response = client.generateToFile("output.mp3", request); ``` ### Text pauses Use text pause markup when you only need silent gaps inside one composed text segment. Put `<|5s|>`, `<|1s|>`, `<|0.3s|>`, or `<|0.34413s|>` directly in the text. The value is interpreted as seconds and must end with `s`. This keeps the pause expression visible in plain text without adding separate pause calls. ```c TypecastSpeechComposer* composer = typecast_speech_composer_create(client); TypecastComposerSettings defaults = {0}; defaults.voice_id = "tc_672c5f5ce59fac2a48faeaee"; defaults.model = TYPECAST_MODEL_SSFM_V30; typecast_speech_composer_defaults(composer, &defaults); typecast_speech_composer_say( composer, "Hello<|5s|>Nice to meet you<|1s|>Today<|2s|>how does the weather feel?", NULL ); TypecastTTSResponse* audio = typecast_speech_composer_generate(composer, TYPECAST_AUDIO_FORMAT_WAV); typecast_tts_response_free(audio); typecast_speech_composer_destroy(composer); ``` ### Multi-speaker composition Use the composer chaining API when one output file needs different voices or per-segment options such as pitch, tempo, prompt, or seed. The composer sends segments to `POST /v1/text-to-speech/compose`, which returns WAV or MP3 directly. Set silence removal explicitly on TTS segments; explicit pauses are preserved. ```c TypecastSpeechComposer* composer = typecast_speech_composer_create(client); TypecastComposerSettings defaults = {0}; defaults.voice_id = "tc_672c5f5ce59fac2a48faeaee"; defaults.model = TYPECAST_MODEL_SSFM_V30; typecast_speech_composer_defaults(composer, &defaults); typecast_speech_composer_say(composer, "Hello there", NULL); typecast_speech_composer_pause(composer, 5.0f); TypecastComposerSettings second = {0}; second.voice_id = "tc_60e5426de8b95f1d3000d7b5"; second.output.audio_pitch = 2; typecast_speech_composer_say(composer, "Nice to meet you", &second); typecast_speech_composer_pause(composer, 2.0f); typecast_speech_composer_say(composer, "How does the weather feel?", NULL); TypecastTTSResponse* audio = typecast_speech_composer_generate(composer, TYPECAST_AUDIO_FORMAT_WAV); /* write audio->audio_data / audio->audio_len to conversation.wav */ typecast_tts_response_free(audio); typecast_speech_composer_destroy(composer); ``` ### Voice Discovery (V2 API) List and filter available voices with enhanced metadata: ```c // Get all voices TypecastVoicesResponse* voices = typecast_get_voices(client, NULL); // Or filter by criteria TypecastModel model = TYPECAST_MODEL_SSFM_V30; TypecastGender gender = TYPECAST_GENDER_FEMALE; TypecastAge age = TYPECAST_AGE_YOUNG_ADULT; TypecastVoicesFilter filter = {0}; filter.model = &model; filter.gender = &gender; filter.age = &age; TypecastVoicesResponse* filtered = typecast_get_voices(client, &filter); // Display voice info if (voices) { for (size_t i = 0; i < voices->count; i++) { TypecastVoice* v = &voices->voices[i]; printf("ID: %s, Name: %s\n", v->voice_id, v->voice_name); printf("Gender: %d, Age: %d\n", v->gender, v->age); for (size_t j = 0; j < v->models_count; j++) { printf("Model: %s, Emotions: ", typecast_model_to_string(v->models[j].version)); for (size_t k = 0; k < v->models[j].emotions_count; k++) { printf("%s ", v->models[j].emotions[k]); } printf("\n"); } if (v->use_cases) { printf("Use cases: "); for (size_t k = 0; k < v->use_cases_count; k++) { printf("%s ", v->use_cases[k]); } printf("\n"); } } typecast_voices_response_free(voices); } ``` ### Multilingual Content The SDK supports 35+ languages with automatic language detection: ```c // Auto-detect language (recommended - omit language field) TypecastTTSRequest request = {0}; request.text = "こんにちは。お元気ですか。"; request.voice_id = "tc_672c5f5ce59fac2a48faeaee"; request.model = TYPECAST_MODEL_SSFM_V30; // language is NULL, so it will be auto-detected TypecastTTSResponse* response = typecast_text_to_speech(client, &request); // Or specify language explicitly using ISO 639-3 code TypecastTTSRequest korean_request = {0}; korean_request.text = "안녕하세요. 반갑습니다."; korean_request.voice_id = "tc_672c5f5ce59fac2a48faeaee"; korean_request.model = TYPECAST_MODEL_SSFM_V30; korean_request.language = "kor"; // ISO 639-3 language code TypecastTTSResponse* korean_response = typecast_text_to_speech(client, &korean_request); ``` ### Streaming Stream audio chunks in real-time for low-latency playback: ```c // Extract raw PCM for real-time playback (skip 44-byte WAV header) static int g_first = 1; static int on_chunk(const uint8_t *data, size_t len, void *user_data) { const uint8_t *pcm = data; size_t pcm_len = len; if (g_first) { pcm += 44; // Skip WAV header pcm_len -= 44; g_first = 0; } // pcm is raw 16-bit mono PCM at 32000 Hz // Feed to your audio output (e.g. PortAudio, ALSA) play_audio(pcm, pcm_len); // your playback function return 0; } ``` **WAV streaming format:** 32000 Hz, 16-bit, mono PCM. The first chunk includes a 44-byte WAV header (size = `0xFFFFFFFF`); subsequent chunks are raw PCM only. For MP3: 320 kbps, 44100 Hz, each chunk is independently decodable. Use `TYPECAST_OUTPUT_STREAM_DEFAULT()` for safe output defaults. ## Timestamp TTS `typecast_text_to_speech_with_timestamps()` wraps `POST /v1/text-to-speech/with-timestamps` and returns the audio together with per-word and per-character alignment data - useful for karaoke highlights, subtitle generation, and lip-sync applications. ### Basic Usage ```c #include "typecast.h" #include #include int main() { TypecastClient* client = typecast_client_create("YOUR_API_KEY"); if (!client) return 1; TypecastTTSWithTimestampsRequest request = {0}; request.text = "Hello. How are you?"; request.voice_id = "tc_60e5426de8b95f1d3000d7b5"; request.model = TYPECAST_MODEL_SSFM_V30; TypecastTTSWithTimestampsResponse* result = typecast_text_to_speech_with_timestamps(client, &request); if (result) { FILE* fp = fopen("output.wav", "wb"); fwrite(result->audio_data, 1, result->audio_size, fp); fclose(fp); printf("Duration: %.3fs\n", result->audio_duration); for (size_t i = 0; i < result->word_count; i++) { printf(" [%.3fs – %.3fs] %s\n", result->words[i].start_time, result->words[i].end_time, result->words[i].text); } typecast_tts_with_timestamps_response_free(result); } typecast_client_destroy(client); return 0; } ``` ### Granularity Set `request.granularity = TYPECAST_GRANULARITY_WORD` (default) or `TYPECAST_GRANULARITY_CHAR` to control the alignment unit. ```c request.granularity = TYPECAST_GRANULARITY_CHAR; /* required for jpn / zho */ ``` ### Subtitle Export ```c // Export SRT (caller must free the returned string) char* srt = typecast_tts_with_timestamps_to_srt(result); FILE* fp = fopen("output.srt", "w"); fputs(srt, fp); fclose(fp); free(srt); // Export WebVTT char* vtt = typecast_tts_with_timestamps_to_vtt(result); // ... same pattern as above free(vtt); ``` **Japanese / Chinese:** Word-level segmentation is not meaningful for languages without whitespace delimiters (jpn, zho). Use `TYPECAST_GRANULARITY_CHAR` for these languages to get character-level alignment. ## Instant Voice Cloning Clone a custom voice from a short audio sample, then pass the returned `uc_` voice ID directly to TTS. ```c TypecastCustomVoice voice; TypecastErrorCode rc = typecast_clone_voice( client, audio_bytes, audio_len, "sample.wav", "My Voice", "ssfm-v30", &voice ); if (rc == TYPECAST_OK) { TypecastTTSRequest request = {0}; request.voice_id = voice.voice_id; request.text = "Hello from my cloned voice!"; request.model = TYPECAST_MODEL_SSFM_V30; TypecastTTSResponse* response = typecast_text_to_speech(client, &request); if (response) { typecast_tts_response_free(response); } typecast_delete_voice(client, voice.voice_id); } ``` Voice cloning audio must be **25 MB or smaller**, the audio duration must be **5-150 seconds**, and the custom voice name must be **1-30 characters**. ## Supported Languages The SDK supports 35+ languages with automatic language detection: | Code | Language | Code | Language | Code | Language | |------|----------|------|----------|------|----------| | `eng` | English | `jpn` | Japanese | `ukr` | Ukrainian | | `kor` | Korean | `ell` | Greek | `ind` | Indonesian | | `spa` | Spanish | `tam` | Tamil | `dan` | Danish | | `deu` | German | `tgl` | Tagalog | `swe` | Swedish | | `fra` | French | `fin` | Finnish | `msa` | Malay | | `ita` | Italian | `zho` | Chinese | `ces` | Czech | | `pol` | Polish | `slk` | Slovak | `por` | Portuguese | | `nld` | Dutch | `ara` | Arabic | `bul` | Bulgarian | | `rus` | Russian | `hrv` | Croatian | `ron` | Romanian | | `ben` | Bengali | `hin` | Hindi | `hun` | Hungarian | | `nan` | Hokkien | `nor` | Norwegian | `pan` | Punjabi | | `tha` | Thai | `tur` | Turkish | `vie` | Vietnamese | | `yue` | Cantonese | | | | | If not specified, the language will be automatically detected from the input text. ## Error Handling The SDK provides specific error codes for handling API errors: ```c #include "typecast.h" TypecastTTSResponse* response = typecast_text_to_speech(client, &request); if (!response) { const TypecastError* err = typecast_client_get_error(client); switch (err->code) { case TYPECAST_ERROR_UNAUTHORIZED: // 401: Invalid API key fprintf(stderr, "Invalid API key: %s\n", err->message); break; case TYPECAST_ERROR_PAYMENT_REQUIRED: // 402: Insufficient credits fprintf(stderr, "Insufficient credits: %s\n", err->message); break; case TYPECAST_ERROR_NOT_FOUND: // 404: Resource not found fprintf(stderr, "Voice not found: %s\n", err->message); break; case TYPECAST_ERROR_UNPROCESSABLE_ENTITY: // 422: Validation error fprintf(stderr, "Validation error: %s\n", err->message); break; case TYPECAST_ERROR_RATE_LIMIT: // 429: Rate limit exceeded fprintf(stderr, "Rate limit exceeded - please retry later\n"); break; case TYPECAST_ERROR_INTERNAL_SERVER: // 500: Server error fprintf(stderr, "Server error: %s\n", err->message); break; default: fprintf(stderr, "API error (%d): %s\n", err->code, err->message); break; } } ``` ### Error Codes | Error Code | Value | Description | |-----------|-------|-------------| | `TYPECAST_OK` | 0 | Success | | `TYPECAST_ERROR_INVALID_PARAM` | -1 | Invalid request parameters | | `TYPECAST_ERROR_OUT_OF_MEMORY` | -2 | Memory allocation failed | | `TYPECAST_ERROR_CURL_INIT` | -3 | Failed to initialize libcurl | | `TYPECAST_ERROR_NETWORK` | -4 | Network error | | `TYPECAST_ERROR_JSON_PARSE` | -5 | JSON parsing error | | `TYPECAST_ERROR_BAD_REQUEST` | 400 | Invalid request | | `TYPECAST_ERROR_UNAUTHORIZED` | 401 | Invalid or missing API key | | `TYPECAST_ERROR_PAYMENT_REQUIRED` | 402 | Insufficient credits | | `TYPECAST_ERROR_NOT_FOUND` | 404 | Resource not found | | `TYPECAST_ERROR_UNPROCESSABLE_ENTITY` | 422 | Validation error | | `TYPECAST_ERROR_RATE_LIMIT` | 429 | Rate limit exceeded | | `TYPECAST_ERROR_INTERNAL_SERVER` | 500 | Server error | ## C++ Wrapper For C++ projects, enable the optional C++ wrapper for a more idiomatic interface: ```cpp #define TYPECAST_CPP_WRAPPER #include "typecast.h" #include #include int main() { try { // Initialize client typecast::Client client("YOUR_API_KEY"); // Convert text to speech typecast::TTSRequest request; request.text = "Hello there! I'm your friendly text-to-speech agent."; request.voiceId = "tc_672c5f5ce59fac2a48faeaee"; request.model = typecast::Model::SSFM_V30; request.language = "eng"; auto response = client.textToSpeech(request); // Save audio file std::ofstream file("output.wav", std::ios::binary); file.write(reinterpret_cast(response.audioData.data()), response.audioData.size()); std::cout << "Audio saved! Duration: " << response.duration << "s\n"; } catch (const typecast::TypecastException& e) { std::cerr << "Error (" << e.code << "): " << e.what() << "\n"; return 1; } return 0; } ``` ## Platform Support The SDK has been verified through automated E2E testing on the following platforms: | Platform | Architecture | glibc | C Standard | Status | |----------|--------------|-------|------------|--------| | **CentOS 6.9** | x86_64 | 2.12 | C99 | Verified | | **CentOS 7** | x86_64 | 2.17 | C11 | Verified | | **Amazon Linux 2** | x86_64 | 2.26 | C11 | Verified | | **Ubuntu 20.04 LTS** | x86_64 | 2.31 | C11 | Verified | | **Debian Bullseye** | x86_64 | 2.31 | C11 | Verified | | **Windows** | x64 | N/A | C11 | Verified | | **macOS** | x86_64 / arm64 | N/A | C11 | Verified | ## Embedded Systems This SDK can be integrated into embedded systems with network connectivity. ### Cross-Compilation ```bash mkdir build-arm && cd build-arm cmake .. \ -DCMAKE_TOOLCHAIN_FILE=../cmake/arm-linux-gnueabihf.cmake \ -DTYPECAST_BUILD_STATIC=ON \ -DTYPECAST_BUILD_SHARED=OFF \ -DCMAKE_BUILD_TYPE=MinSizeRel cmake --build . ``` ```bash mkdir build-arm64 && cd build-arm64 cmake .. \ -DCMAKE_SYSTEM_NAME=Linux \ -DCMAKE_SYSTEM_PROCESSOR=aarch64 \ -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc \ -DTYPECAST_BUILD_STATIC=ON \ -DTYPECAST_BUILD_SHARED=OFF \ -DCMAKE_BUILD_TYPE=MinSizeRel cmake --build . ``` ### Memory Requirements | Component | Approximate Size | |-----------|------------------| | Static library (MinSizeRel) | ~50 KB | | Runtime heap per client | ~8 KB | | TTS response buffer | Variable (audio size) | | JSON parsing buffer | ~4 KB | ## Unreal Engine Integration This SDK is designed for seamless integration with Unreal Engine 4.27+ and Unreal Engine 5.x. Build as a static library: ```bash mkdir build && cd build cmake .. -DTYPECAST_BUILD_STATIC=ON -DTYPECAST_BUILD_SHARED=OFF -DCMAKE_BUILD_TYPE=Release cmake --build . --config Release ``` Create a plugin in your Unreal project: ``` Plugins/ └── TypecastTTS/ ├── Source/TypecastTTS/ │ ├── Private/ │ ├── Public/ │ └── ThirdParty/Typecast/ │ ├── include/typecast.h │ └── lib/Win64/typecast_static.lib ├── TypecastTTS.uplugin └── TypecastTTS.Build.cs ``` Add library linking to your `Build.cs`: ```csharp // Add include path PublicIncludePaths.Add(Path.Combine(ThirdPartyPath, "include")); PublicDefinitions.Add("TYPECAST_STATIC"); // Link static library (platform-specific) if (Target.Platform == UnrealTargetPlatform.Win64) { PublicAdditionalLibraries.Add( Path.Combine(LibPath, "Win64", "typecast_static.lib")); AddEngineThirdPartyPrivateStaticDependencies(Target, "libcurl"); } ``` For complete Unreal Engine integration guide including Blueprint support and audio playback, see the [README](https://github.com/neosapience/typecast-sdk/tree/main/typecast-c) in the SDK repository. ## API Reference ### Client Functions | Function | Description | |----------|-------------| | `typecast_client_create(api_key)` | Create client with API key | | `typecast_client_create_with_host(api_key, host)` | Create client with custom host | | `typecast_client_destroy(client)` | Destroy client and free resources | | `typecast_client_get_error(client)` | Get last error information | ### Text-to-Speech Functions | Function | Description | |----------|-------------| | `typecast_text_to_speech(client, request)` | Convert text to speech audio | | `typecast_generate_to_file(client, path, request)` | Generate speech and save it directly to a local file | | `typecast_tts_response_free(response)` | Free TTS response memory | ### Voice Functions | Function | Description | |----------|-------------| | `typecast_get_voices(client, filter)` | Get available voices (optionally filtered) | | `typecast_get_voice(client, voice_id)` | Get a specific voice by ID | | `typecast_clone_voice(client, audio, audio_len, filename, name, model, out)` | Create a custom voice via instant cloning | | `typecast_delete_voice(client, voice_id)` | Delete a custom cloned voice | | `typecast_voices_response_free(response)` | Free voices response memory | | `typecast_voice_free(voice)` | Free single voice memory | ### Utility Functions | Function | Description | |----------|-------------| | `typecast_version()` | Get library version string | | `typecast_model_to_string(model)` | Convert model enum to string | | `typecast_emotion_to_string(emotion)` | Convert emotion enum to string | | `typecast_audio_format_to_string(format)` | Convert format enum to string | | `typecast_error_message(code)` | Get error message for error code | ### TypecastTTSRequest Fields | Field | Type | Required | Description | |-------|------|----------|-------------| | `text` | `const char*` | ✓ | Text to synthesize (max 2000 chars) | | `voice_id` | `const char*` | ✓ | Voice ID (format: `tc_*` or `uc_*`) | | `model` | `TypecastModel` | ✓ | TTS model (`SSFM_V21` or `SSFM_V30`) | | `language` | `const char*` | | ISO 639-3 code (auto-detected if NULL) | | `prompt` | `TypecastPrompt*` | | Emotion settings | | `output` | `TypecastOutput*` | | Audio output settings | | `seed` | `unsigned int` | | Unsigned integer seed for reproducibility (≥ 0) | ### TypecastTTSResponse Fields | Field | Type | Description | |-------|------|-------------| | `audio_data` | `uint8_t*` | Generated audio data | | `audio_size` | `size_t` | Size of audio data in bytes | | `duration` | `float` | Audio duration in seconds | | `format` | `TypecastAudioFormat` | Audio format (wav or mp3) | ## Control silence duration Requires **1.2.13 or later**. `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. C preserves the existing public-struct ABI. With `client` and a `TypecastTTSRequest request` prepared, use the extension function; do not add a field to an existing struct. `NULL` disables processing; an integer pointer preserves explicit values including `0`. ```c int remaining_silence_ms = 300; TypecastTTSResponse* audio = typecast_text_to_speech_with_silence( client, &request, &remaining_silence_ms ); if (audio != NULL) typecast_tts_response_free(audio); ``` Use `typecast_text_to_speech_stream_with_silence` for streaming, `typecast_text_to_speech_with_timestamps_and_silence` for timestamps, and `typecast_generate_to_file_with_silence` for files. Composer defaults and per-segment values use `typecast_speech_composer_defaults_with_silence` and `typecast_speech_composer_say_with_silence`. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Swift The official Swift library for the [Typecast API](https://typecast.ai). Convert text to lifelike speech using AI-powered voices. Compatible with Swift 5.9+ and supports all Apple platforms: iOS, macOS, tvOS, watchOS, and visionOS. Typecast Swift SDK Typecast Swift SDK Source Code ## Requirements | Platform | Minimum Version | |----------|-----------------| | iOS | 13.0+ | | macOS | 10.15+ | | tvOS | 13.0+ | | watchOS | 6.0+ | | visionOS | 1.0+ | | Swift | 5.9+ | ## Installation Clone the tagged release, then reference its Swift package directory locally: ```bash git clone --branch typecast-swift/v0.3.14 --depth 1 https://github.com/neosapience/typecast-sdk.git ``` ```swift dependencies: [ .package(path: "typecast-sdk/typecast-swift") ], targets: [ .target( name: "YourTarget", dependencies: [ .product(name: "Typecast", package: "typecast-swift") ] ) ] ``` In Xcode, use **File** → **Add Package Dependencies...** → **Add Local...** and select the cloned `typecast-swift` directory. Latest registered version: **typecast-swift/v0.3.14** in the SDK Git tags. Make sure you have **Swift 5.9 or higher** installed. The SDK uses Swift Concurrency (async/await) which requires this minimum version. ## Quick Start ```swift import AVFoundation import Typecast let client = TypecastClient(apiKey: "YOUR_API_KEY") var audioPlayer: AVAudioPlayer? // Simple usage with convenience method let audio = try await client.speak( "Hello! I'm your friendly text-to-speech assistant.", voiceId: "tc_672c5f5ce59fac2a48faeaee" ) // Play audio directly from data audioPlayer = try AVAudioPlayer(data: audio.audioData) audioPlayer?.play() print("Duration: \(audio.duration)s, Format: \(audio.format.rawValue)") ``` ## Features The Typecast Swift SDK provides powerful features for text-to-speech conversion: - **Multiple Voice Models**: Support for `ssfm-v30` (latest) and `ssfm-v21` AI voice models - **Multi-language Support**: 35+ languages including English, Korean, Spanish, Japanese, Chinese, and more - **Emotion Control**: Preset emotions (normal, happy, sad, angry, whisper, toneup, tonedown) or smart context-aware inference - **Audio Customization**: Control loudness (LUFS -70 to 0), pitch (-12 to +12 semitones), tempo (0.5x to 2.0x), and format (WAV/MP3) - **Voice Discovery**: V2 Voices API with filtering by model, gender, age, and use cases - **Instant Voice Cloning**: Upload a WAV/MP3 sample and create a custom voice ID - **Swift Concurrency**: Full async/await support for modern Swift development - **Thread-Safe**: All types conform to `Sendable` for safe concurrent usage - **Timestamp TTS**: Word- and character-level alignment data for subtitles, karaoke, and lip-sync - **Cross-Platform**: Works on iOS, macOS, tvOS, watchOS, and visionOS - **Streaming**: Real-time chunked audio delivery for low-latency playback ## Voice Recommendations Use `recommendVoices` when you know the desired style but not the exact `voice_id`. ```swift let voices = try await client.recommendVoices( query: "warm female voice for a product tutorial", count: 3 ) for voice in voices { print("\(voice.voiceId) \(voice.voiceName) \(voice.score)") } ``` Recommendation results contain only `voiceId`, `voiceName`, and `score`. Use `getVoice(voiceId:)` or `getVoices(filter:)` when you need detailed metadata such as supported models, emotions, gender, age, or use cases. ## Configuration Initialize the client with your API key: ```swift import Typecast // Direct initialization let client = TypecastClient(apiKey: "your-api-key") // With custom base URL let client = TypecastClient( apiKey: "your-api-key", baseURL: "https://api.typecast.ai" ) // Using configuration struct let config = TypecastConfiguration(apiKey: "your-api-key") let client = TypecastClient(configuration: config) ``` When requests go through your own proxy, set `baseURL` to the proxy endpoint and omit `apiKey`. The SDK will not send the `X-API-KEY` header for nil or empty keys. Requests to the default Typecast host still require an API key. ```swift Proxy without API key let client = TypecastClient( baseURL: "https://your-proxy.example.com" ) ``` ## Advanced Usage ### Emotion Control (ssfm-v30) ssfm-v30 offers two emotion control modes: **Preset** and **Smart**. Let the AI infer emotion from context: ```swift let request = TTSRequest( voiceId: "tc_672c5f5ce59fac2a48faeaee", // Find voice IDs at https://studio.typecast.ai/developers/api/voices text: "Everything is going to be okay.", model: .ssfmV30, prompt: .smart(SmartPrompt( previousText: "I just got the best news!", // Optional context nextText: "I can't wait to celebrate!" // Optional context )) ) let response = try await client.textToSpeech(request) audioPlayer = try AVAudioPlayer(data: response.audioData) audioPlayer?.play() ``` Explicitly set emotion with preset values: ```swift let request = TTSRequest( voiceId: "tc_672c5f5ce59fac2a48faeaee", text: "I am so excited to show you these features!", model: .ssfmV30, prompt: .preset(PresetPrompt( emotionPreset: .happy, // normal, happy, sad, angry, whisper, toneup, tonedown emotionIntensity: 1.5 // Range: 0.0 to 2.0 )) ) let response = try await client.textToSpeech(request) audioPlayer = try AVAudioPlayer(data: response.audioData) audioPlayer?.play() ``` Use the convenience method for quick emotion control: ```swift let audio = try await client.speak( "I'm so excited!", voiceId: "tc_672c5f5ce59fac2a48faeaee", emotion: .happy, intensity: 1.5 ) audioPlayer = try AVAudioPlayer(data: audio.audioData) audioPlayer?.play() ``` ### Audio Customization Control loudness, pitch, tempo, and output format: ```swift let request = TTSRequest( voiceId: "tc_672c5f5ce59fac2a48faeaee", text: "Customized audio output!", model: .ssfmV30, output: OutputSettings( targetLufs: -14.0, // Range: -70 to 0 (LUFS) audioPitch: 2, // Range: -12 to +12 semitones audioTempo: 1.2, // Range: 0.5x to 2.0x audioFormat: .mp3 // Options: .wav, .mp3 ), seed: 42 // Unsigned seed for reproducible results ) let response = try await client.textToSpeech(request) audioPlayer = try AVAudioPlayer(data: response.audioData) audioPlayer?.play() print("Duration: \(response.duration)s, Format: \(response.format.rawValue)") ``` ### Generate audio to a file Use `generateToFile` when you want the SDK to synthesize speech and write the audio bytes directly to a local file. The model defaults to `ssfm-v30`, and `.mp3` / `.wav` extensions infer the output format when no output format is set. Browse available voice IDs on the [Voices](https://studio.typecast.ai/developers/api/voices) page. ```swift try await client.generateToFile( "output.mp3", request: GenerateToFileRequest( voiceId: "tc_672c5f5ce59fac2a48faeaee", // Find voice IDs at https://studio.typecast.ai/developers/api/voices text: "Hello from Typecast." ) ) ``` ### Text pauses Use text pause markup when you only need silent gaps inside one composed text segment. Put `<|5s|>`, `<|1s|>`, `<|0.3s|>`, or `<|0.34413s|>` directly in the text. The value is interpreted as seconds and must end with `s`. This keeps the pause expression visible in plain text without adding separate pause calls. ```swift let audio = try await client.composeSpeech() .defaults(ComposerSettings(voiceId: "tc_672c5f5ce59fac2a48faeaee", model: .ssfmV30)) .say("Hello<|5s|>Nice to meet you<|1s|>Today<|2s|>how does the weather feel?") .generate() ``` ### Multi-speaker composition Use the composer chaining API when one output file needs different voices or per-segment options such as pitch, tempo, prompt, or seed. The composer sends segments to `POST /v1/text-to-speech/compose`, which returns WAV or MP3 directly. Set silence removal explicitly on TTS segments; explicit pauses are preserved. ```swift let audio = try await client.composeSpeech() .defaults(ComposerSettings(voiceId: "tc_672c5f5ce59fac2a48faeaee", model: .ssfmV30)) .say("Hello there") .pause(5) .say("Nice to meet you", overrides: ComposerSettings( voiceId: "tc_60e5426de8b95f1d3000d7b5", output: OutputSettings(audioPitch: 2) )) .say("Today") .pause(2) .say("How does the weather feel?") .generate() try audio.audioData.write(to: URL(fileURLWithPath: "conversation.wav")) ``` ### Voice Discovery (V2 API) List and filter available voices with enhanced metadata: ```swift // Get all voices let voices = try await client.getVoices() // Filter by criteria let filteredVoices = try await client.getVoices(filter: VoicesV2Filter( model: .ssfmV30, gender: .female, age: .youngAdult )) // Get a specific voice let voice = try await client.getVoice(voiceId: "tc_672c5f5ce59fac2a48faeaee") // Display voice info print("ID: \(voice.voiceId), Name: \(voice.voiceName)") print("Gender: \(voice.gender?.rawValue ?? "N/A"), Age: \(voice.age?.rawValue ?? "N/A")") for model in voice.models { print("Model: \(model.version.rawValue), Emotions: \(model.emotions.joined(separator: ", "))") } if let useCases = voice.useCases { print("Use cases: \(useCases.joined(separator: ", "))") } ``` ### Multilingual Content The SDK supports 35+ languages with automatic language detection: ```swift // Auto-detect language (recommended) let request = TTSRequest( voiceId: "tc_672c5f5ce59fac2a48faeaee", text: "こんにちは。お元気ですか。", model: .ssfmV30 ) let response = try await client.textToSpeech(request) // Or specify language explicitly let koreanRequest = TTSRequest( voiceId: "tc_672c5f5ce59fac2a48faeaee", text: "안녕하세요. 반갑습니다.", model: .ssfmV30, language: .korean // Explicit language code ) let koreanResponse = try await client.textToSpeech(koreanRequest) audioPlayer = try AVAudioPlayer(data: koreanResponse.audioData) audioPlayer?.play() ``` ### Streaming Stream audio chunks in real-time for low-latency playback: ```swift import AVFoundation import Typecast let engine = AVAudioEngine() let playerNode = AVAudioPlayerNode() let format = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: 32000, channels: 1, interleaved: true)! engine.attach(playerNode) engine.connect(playerNode, to: engine.mainMixerNode, format: format) try engine.start() playerNode.play() let stream = try await client.textToSpeechStream(request) var first = true for try await chunk in stream { var pcmData = chunk if first { pcmData = chunk.dropFirst(44) // Skip 44-byte WAV header first = false } let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: AVAudioFrameCount(pcmData.count / 2))! buffer.frameLength = buffer.frameCapacity pcmData.withUnsafeBytes { ptr in buffer.int16ChannelData!.pointee.update(from: ptr.bindMemory(to: Int16.self).baseAddress!, count: Int(buffer.frameLength)) } playerNode.scheduleBuffer(buffer) } ``` **WAV streaming format:** 32000 Hz, 16-bit, mono PCM. The first chunk includes a 44-byte WAV header (size = `0xFFFFFFFF`); subsequent chunks are raw PCM only. For MP3: 320 kbps, 44100 Hz, each chunk is independently decodable. Use `Typecast.OutputStream` to avoid collision with `Foundation.OutputStream`. ## Timestamp TTS `textToSpeechWithTimestamps()` wraps `POST /v1/text-to-speech/with-timestamps` and returns the audio together with per-word and per-character alignment data - useful for karaoke highlights, subtitle generation, and lip-sync applications. ### Basic Usage ```swift import Typecast let client = TypecastClient(apiKey: "YOUR_API_KEY") let request = TTSRequestWithTimestamps( voiceId: "tc_60e5426de8b95f1d3000d7b5", text: "Hello. How are you?", model: .ssfmV30 ) let result = try await client.textToSpeechWithTimestamps(request) audioPlayer = try AVAudioPlayer(data: result.audioData) audioPlayer?.play() print("Duration: \(result.audioDuration)s") for word in result.words { print(" [\(word.startTime)s – \(word.endTime)s] \(word.text)") } ``` ### Granularity Pass `granularity: .word` (default) or `granularity: .char` to control the alignment unit. ```swift let request = TTSRequestWithTimestamps( voiceId: "tc_60e5426de8b95f1d3000d7b5", text: "Hello. How are you?", model: .ssfmV30, granularity: .char // required for Japanese / Chinese ) ``` ### Subtitle Export ```swift let srt = result.toSrt() print(srt) let vtt = result.toVtt() print(vtt) ``` **Japanese / Chinese:** Word-level segmentation is not meaningful for languages without whitespace delimiters (jpn, zho). Use `.char` granularity for these languages to get character-level alignment. ## Instant Voice Cloning Clone a custom voice from a short audio sample, then pass the returned `uc_` voice ID directly to TTS. ```swift import AVFoundation import Foundation import Typecast let client = TypecastClient(apiKey: "YOUR_API_KEY") var audioPlayer: AVAudioPlayer? let audioData = try Data(contentsOf: URL(fileURLWithPath: "sample.wav")) let voice = try await client.cloneVoice( audio: audioData, filename: "sample.wav", name: "My Voice", model: "ssfm-v30" ) let response = try await client.speak( "Hello from my cloned voice!", voiceId: voice.voiceId ) audioPlayer = try AVAudioPlayer(data: response.audioData) audioPlayer?.play() try await client.deleteVoice(voice.voiceId) ``` Voice cloning audio must be **25 MB or smaller**, the audio duration must be **5-150 seconds**, and the custom voice name must be **1-30 characters**. ## Supported Languages The SDK supports 35+ languages with automatic language detection: | Code | Language | Code | Language | Code | Language | |------|----------|------|----------|------|----------| | `.english` | English | `.japanese` | Japanese | `.ukrainian` | Ukrainian | | `.korean` | Korean | `.greek` | Greek | `.indonesian` | Indonesian | | `.spanish` | Spanish | `.tamil` | Tamil | `.danish` | Danish | | `.german` | German | `.tagalog` | Tagalog | `.swedish` | Swedish | | `.french` | French | `.finnish` | Finnish | `.malay` | Malay | | `.italian` | Italian | `.chinese` | Chinese | `.czech` | Czech | | `.polish` | Polish | `.slovak` | Slovak | `.portuguese` | Portuguese | | `.dutch` | Dutch | `.arabic` | Arabic | `.bulgarian` | Bulgarian | | `.russian` | Russian | `.croatian` | Croatian | `.romanian` | Romanian | | `.bengali` | Bengali | `.hindi` | Hindi | `.hungarian` | Hungarian | | `.minNan` | Hokkien | `.norwegian` | Norwegian | `.punjabi` | Punjabi | | `.thai` | Thai | `.turkish` | Turkish | `.vietnamese` | Vietnamese | | `.cantonese` | Cantonese | | | | | If not specified, the language will be automatically detected from the input text. ## Error Handling The SDK provides a comprehensive `TypecastError` enum for handling API errors: ```swift import Typecast do { let response = try await client.textToSpeech(request) } catch let error as TypecastError { switch error { case .unauthorized(let message): // 401: Invalid API key print("Invalid API key: \(message)") case .paymentRequired(let message): // 402: Insufficient credits print("Insufficient credits: \(message)") case .notFound(let message): // 404: Resource not found print("Voice not found: \(message)") case .validationError(let message): // 422: Validation error print("Validation error: \(message)") case .rateLimitExceeded(let message): // 429: Rate limit exceeded print("Rate limit exceeded: \(message)") case .serverError(let message): // 500: Server error print("Server error: \(message)") case .networkError(let underlyingError): // Network connectivity issues print("Network error: \(underlyingError.localizedDescription)") case .invalidResponse(let message): // Invalid response from server print("Invalid response: \(message)") default: print("Error: \(error.localizedDescription)") } // Access status code if available if let statusCode = error.statusCode { print("HTTP Status: \(statusCode)") } } ``` ### Error Types | Error | Status Code | Description | |-------|-------------|-------------| | `.badRequest` | 400 | Invalid request parameters | | `.unauthorized` | 401 | Invalid or missing API key | | `.paymentRequired` | 402 | Insufficient credits | | `.notFound` | 404 | Resource not found | | `.validationError` | 422 | Validation error | | `.rateLimitExceeded` | 429 | Rate limit exceeded | | `.serverError` | 500 | Server error | | `.networkError` | - | Network connectivity issues | | `.invalidResponse` | - | Invalid response from server | ## Platform-Specific Usage ### iOS ```swift import Typecast import AVFoundation class TTSManager { private let client = TypecastClient(apiKey: "YOUR_API_KEY") private var audioPlayer: AVAudioPlayer? func speak(_ text: String) async throws { let audio = try await client.speak(text, voiceId: "tc_672c5f5ce59fac2a48faeaee") // Play audio directly from data audioPlayer = try AVAudioPlayer(data: audio.audioData) audioPlayer?.play() } } ``` ### macOS ```swift import Typecast import AppKit import AVFoundation class MacTTSManager { private let client = TypecastClient(apiKey: "YOUR_API_KEY") private var audioPlayer: AVAudioPlayer? func speak(_ text: String) async throws { let audio = try await client.speak(text, voiceId: "tc_672c5f5ce59fac2a48faeaee") audioPlayer = try AVAudioPlayer(data: audio.audioData) audioPlayer?.play() } } ``` ### watchOS ```swift import Typecast import AVFoundation class WatchTTSManager { private let client = TypecastClient(apiKey: "YOUR_API_KEY") private var audioPlayer: AVAudioPlayer? func speak(_ text: String) async throws { let audio = try await client.speak(text, voiceId: "tc_672c5f5ce59fac2a48faeaee") audioPlayer = try AVAudioPlayer(data: audio.audioData) audioPlayer?.play() } } ``` ## API Reference ### TypecastClient Methods | Method | Description | |--------|-------------| | `textToSpeech(_:)` | Convert text to speech audio | | `generateToFile(_:request:)` | Generate speech and save it directly to a local file | | `speak(_:voiceId:model:)` | Simple TTS with minimal parameters | | `speak(_:voiceId:model:emotion:intensity:)` | TTS with emotion preset | | `cloneVoice(audio:filename:name:model:)` | Create a custom voice via instant cloning | | `cloneVoice(audioFileURL:name:model:)` | Create a custom voice from a local audio file | | `deleteVoice(_:)` | Delete a custom cloned voice | | `getVoices(filter:)` | Get available voices with optional filter | | `getVoice(voiceId:)` | Get a specific voice by ID | ### TTSRequest Fields | Field | Type | Required | Description | |-------|------|----------|-------------| | `voiceId` | `String` | ✓ | Voice ID (format: `tc_*` or `uc_*`) | | `text` | `String` | ✓ | Text to synthesize (max 2000 chars) | | `model` | `TTSModel` | ✓ | TTS model (`.ssfmV21` or `.ssfmV30`) | | `language` | `LanguageCode` | | Language code (auto-detected if omitted) | | `prompt` | `TTSPrompt` | | Emotion settings (`.basic`, `.preset`, or `.smart`) | | `output` | `OutputSettings` | | Audio output settings | | `seed` | `UInt32` | | Unsigned integer seed for reproducibility (≥ 0) | ### TTSResponse Fields | Field | Type | Description | |-------|------|-------------| | `audioData` | `Data` | Generated audio data | | `duration` | `TimeInterval` | Audio duration in seconds | | `format` | `AudioFormat` | Audio format (`.wav` or `.mp3`) | ## Control silence duration Requires **0.3.14 or later**. `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. Pass these output settings to the corresponding request's `output`. For streaming, use the streaming output type. ```swift let output = OutputSettings(removeSilenceMs: 300) let streamOutput = Typecast.OutputStream(removeSilenceMs: 300) ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Zig The official Zig library for the [Typecast API](https://studio.typecast.ai/developers/api). Convert text to lifelike speech using AI-powered voices. Pure Zig implementation - no C dependencies. Uses only `std.http.Client` and `std.json` from the Zig standard library. Typecast Zig SDK Source Code Zig Package (via zig fetch) ## Installation Add the dependency using `zig fetch`: ```bash zig fetch --save "https://github.com/neosapience/typecast-sdk/archive/refs/tags/typecast-zig/v0.2.12.tar.gz" ``` Latest registered version: **typecast-zig/v0.2.12** in the SDK Git tags. Then add the import in your `build.zig`: ```zig const typecast_dep = b.dependency("typecast_zig", .{ .target = target, .optimize = optimize, }); exe.root_module.addImport("typecast", typecast_dep.module("typecast")); ``` ## Quick Start ```zig const std = @import("std"); const typecast = @import("typecast"); pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); // Initialize client (reads TYPECAST_API_KEY from environment) var client = typecast.Client.init(allocator, .{ .api_key = std.posix.getenv("TYPECAST_API_KEY") orelse return error.MissingApiKey, }); defer client.deinit(); // Convert text to speech const response = try client.textToSpeech(.{ .voice_id = "tc_672c5f5ce59fac2a48faeaee", // Find voice IDs at https://studio.typecast.ai/developers/api/voices .text = "Hello there! I'm your friendly text-to-speech agent.", .model = .ssfm_v30, }); defer allocator.free(response.audio_data); // Save audio file const file = try std.fs.cwd().createFile("output.wav", .{}); defer file.close(); try file.writeAll(response.audio_data); std.debug.print("Saved {d} bytes, duration: {d:.1}s\n", .{ response.audio_data.len, response.duration, }); } ``` ## Features The Typecast Zig SDK provides powerful features for text-to-speech conversion: - **Multiple Voice Models**: Support for `ssfm-v30` (latest) and `ssfm-v21` AI voice models - **Multi-language Support**: 35+ languages including English, Korean, Spanish, Japanese, Chinese, and more - **Emotion Control**: Preset emotions (normal, happy, sad, angry, whisper, toneup, tonedown) or smart context-aware inference - **Audio Customization**: Control loudness (LUFS -70 to 0), pitch (-12 to +12 semitones), tempo (0.5x to 2.0x), and format (WAV/MP3) - **Voice Discovery**: V2 Voices API with filtering by model, gender, age, and use cases - **Instant Voice Cloning**: Upload a WAV/MP3 sample and create a custom voice ID - **Timestamp TTS**: Word- and character-level alignment data for subtitles, karaoke, and lip-sync - **Pure Zig**: Zero external dependencies, uses only the standard library - **Streaming**: Real-time chunked audio delivery for low-latency playback via callback - **Explicit Memory Management**: Caller-supplied allocator with clear ownership semantics ## Voice Recommendations Use `recommendVoices` when you know the desired style but not the exact `voice_id`. ```zig const voices = try client.recommendVoices( "warm female voice for a product tutorial", 3, ); defer { for (voices) |voice| { allocator.free(voice.voice_id); allocator.free(voice.voice_name); } allocator.free(voices); } for (voices) |voice| { std.debug.print("{s} {s} {d:.3}\n", .{ voice.voice_id, voice.voice_name, voice.score, }); } ``` Recommendation results contain only `voice_id`, `voice_name`, and `score`. Use `getVoiceV2` or `getVoicesV2` when you need detailed metadata such as supported models, emotions, gender, age, or use cases. ## Configuration Set your API key via environment variable or pass directly: ```zig const typecast = @import("typecast"); // Using environment variable (recommended) // export TYPECAST_API_KEY="your-api-key-here" var client = typecast.Client.init(allocator, .{ .api_key = std.posix.getenv("TYPECAST_API_KEY") orelse return error.MissingApiKey, }); defer client.deinit(); ``` ```zig // Or pass directly var client = typecast.Client.init(allocator, .{ .api_key = "your-api-key-here", }); defer client.deinit(); ``` ```zig // Custom base URL var client = typecast.Client.init(allocator, .{ .api_key = "your-api-key-here", .base_url = "https://custom-api.example.com", }); defer client.deinit(); ``` When requests go through your own proxy, set `base_url` to the proxy endpoint and omit `api_key`. The SDK will not send the `X-API-KEY` header for empty or missing keys. Requests to the default Typecast host still require an API key. ```zig Proxy without API key var client = typecast.Client.init(allocator, .{ .base_url = "https://your-proxy.example.com", }); defer client.deinit(); ``` ## Advanced Usage ### Emotion Control (ssfm-v30) ssfm-v30 offers two emotion control modes: **Preset** and **Smart**. Let the AI infer emotion from context: ```zig const response = try client.textToSpeech(.{ .voice_id = "tc_672c5f5ce59fac2a48faeaee", .text = "Everything is going to be okay.", .model = .ssfm_v30, .prompt = .{ .smart = .{ .previous_text = "I just got the best news!", .next_text = "I can't wait to celebrate!", } }, }); defer allocator.free(response.audio_data); ``` Explicitly set emotion with preset values: ```zig const response = try client.textToSpeech(.{ .voice_id = "tc_672c5f5ce59fac2a48faeaee", .text = "I am so excited to show you these features!", .model = .ssfm_v30, .prompt = .{ .preset = .{ .emotion_preset = .happy, .emotion_intensity = 1.5, } }, }); defer allocator.free(response.audio_data); ``` ### Audio Customization Control loudness, pitch, tempo, and output format: ```zig const response = try client.textToSpeech(.{ .voice_id = "tc_672c5f5ce59fac2a48faeaee", .text = "Customized audio output!", .model = .ssfm_v30, .output = .{ .target_lufs = -14.0, .audio_pitch = 2, .audio_tempo = 1.2, .audio_format = .mp3, }, .seed = 42, }); defer allocator.free(response.audio_data); ``` ### Generate audio to a file Use `generateToFile` when you want the SDK to synthesize speech and write the audio bytes directly to a local file. The model defaults to `ssfm-v30`, and `.mp3` / `.wav` extensions infer the output format when no output format is set. Browse available voice IDs on the [Voices](https://studio.typecast.ai/developers/api/voices) page. ```zig const response = try client.generateToFile("output.mp3", .{ .text = "Hello from Typecast.", .voice_id = "tc_672c5f5ce59fac2a48faeaee", // Find voice IDs at https://studio.typecast.ai/developers/api/voices }); defer allocator.free(response.audio_data); ``` ### Text pauses Use text pause markup when you only need silent gaps inside one composed text segment. Put `<|5s|>`, `<|1s|>`, `<|0.3s|>`, or `<|0.34413s|>` directly in the text. The value is interpreted as seconds and must end with `s`. This keeps the pause expression visible in plain text without adding separate pause calls. ```zig var composer = client.composeSpeech(); try composer.defaults(.{ .voice_id = "tc_672c5f5ce59fac2a48faeaee", .model = .ssfm_v30 }); try composer.say("Hello<|5s|>Nice to meet you<|1s|>Today<|2s|>how does the weather feel?", .{}); const audio = try composer.generate(allocator); defer allocator.free(audio.audio_data); ``` ### Multi-speaker composition Use the composer chaining API when one output file needs different voices or per-segment options such as pitch, tempo, prompt, or seed. The composer sends segments to `POST /v1/text-to-speech/compose`, which returns WAV or MP3 directly. Set silence removal explicitly on TTS segments; explicit pauses are preserved. ```zig var composer = client.composeSpeech(); defer composer.deinit(); try composer.defaults(.{ .voice_id = "tc_672c5f5ce59fac2a48faeaee", .model = .ssfm_v30, }); try composer.say("Hello there", .{}); try composer.pause(5); try composer.say("Nice to meet you", .{ .voice_id = "tc_60e5426de8b95f1d3000d7b5", .output = .{ .audio_pitch = 2 }, }); try composer.pause(2); try composer.say("How does the weather feel?", .{}); const audio = try composer.generate(.wav); defer audio.deinit(allocator); try std.fs.cwd().writeFile(.{ .sub_path = "conversation.wav", .data = audio.audio_data }); ``` ### Voice Discovery (V2 API) List and filter available voices with enhanced metadata: ```zig // Get all voices const voices = try client.getVoicesV2(null); defer allocator.free(voices); // Filter by model const filtered = try client.getVoicesV2(.{ .model = .ssfm_v30 }); defer allocator.free(filtered); for (voices) |voice| { std.debug.print("ID: {s}, Name: {s}\n", .{ voice.voice_id, voice.voice_name }); } // Get a specific voice const voice = try client.getVoiceV2("tc_672c5f5ce59fac2a48faeaee", null); std.debug.print("Voice: {s}\n", .{voice.voice_name}); ``` ### Streaming Stream audio chunks in real-time for low-latency playback via callback: ```zig try client.textToSpeechStream(.{ .voice_id = "tc_672c5f5ce59fac2a48faeaee", .text = "Stream this text as audio in real time.", .model = .ssfm_v30, }, struct { var first = true; fn onChunk(chunk: []const u8) anyerror!void { var data = chunk; if (first) { data = chunk[44..]; // Skip 44-byte WAV header first = false; } // data is raw 16-bit mono PCM at 32000 Hz // Feed to your audio output } }.onChunk); ``` **WAV streaming format:** 32000 Hz, 16-bit, mono PCM. The first chunk includes a 44-byte WAV header (size = `0xFFFFFFFF`); subsequent chunks are raw PCM only. For MP3: 320 kbps, 44100 Hz, each chunk is independently decodable. ## Timestamp TTS `textToSpeechWithTimestamps()` wraps `POST /v1/text-to-speech/with-timestamps` and returns the audio together with per-word and per-character alignment data - useful for karaoke highlights, subtitle generation, and lip-sync applications. ### Basic Usage ```zig const std = @import("std"); const typecast = @import("typecast"); pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); var client = typecast.Client.init(allocator, .{ .api_key = std.posix.getenv("TYPECAST_API_KEY") orelse return error.MissingApiKey, }); defer client.deinit(); const result = try client.textToSpeechWithTimestamps(.{ .voice_id = "tc_60e5426de8b95f1d3000d7b5", .text = "Hello. How are you?", .model = .ssfm_v30, }); defer allocator.free(result.audio_data); const file = try std.fs.cwd().createFile("output.wav", .{}); defer file.close(); try file.writeAll(result.audio_data); std.debug.print("Duration: {d:.3}s\n", .{result.audio_duration}); for (result.words) |w| { std.debug.print(" [{d:.3}s – {d:.3}s] {s}\n", .{ w.start_time, w.end_time, w.text }); } } ``` ### Granularity Set `granularity: .word` (default) or `granularity: .char` to control the alignment unit. ```zig const result = try client.textToSpeechWithTimestamps(.{ .voice_id = "tc_60e5426de8b95f1d3000d7b5", .text = "Hello. How are you?", .model = .ssfm_v30, .granularity = .char, // required for jpn / zho }); ``` ### Subtitle Export ```zig const srt = try result.toSrt(allocator); defer allocator.free(srt); try std.fs.cwd().writeFile("output.srt", srt); const vtt = try result.toVtt(allocator); defer allocator.free(vtt); try std.fs.cwd().writeFile("output.vtt", vtt); ``` **Japanese / Chinese:** Word-level segmentation is not meaningful for languages without whitespace delimiters (jpn, zho). Use `.char` granularity for these languages to get character-level alignment. ## Instant Voice Cloning Clone a custom voice from a short audio sample, then pass the returned `uc_` voice ID directly to TTS. ```zig const audio_file = try std.fs.cwd().openFile("sample.wav", .{}); defer audio_file.close(); const audio = try audio_file.readToEndAlloc(allocator, typecast.CLONING_MAX_FILE_SIZE); defer allocator.free(audio); const voice = try client.cloneVoice( allocator, audio, "sample.wav", "My Voice", "ssfm-v30", ); defer { allocator.free(voice.voice_id); allocator.free(voice.name); allocator.free(voice.model); } const response = try client.textToSpeech(.{ .voice_id = voice.voice_id, .text = "Hello from my cloned voice!", .model = .ssfm_v30, }); defer allocator.free(response.audio_data); try client.deleteVoice(voice.voice_id); ``` Voice cloning audio must be **25 MB or smaller**, the audio duration must be **5-150 seconds**, and the custom voice name must be **1-30 characters**. ## Supported Languages The SDK supports 35+ languages with automatic language detection: | Code | Language | Code | Language | Code | Language | |------|----------|------|----------|------|----------| | `eng` | English | `jpn` | Japanese | `ukr` | Ukrainian | | `kor` | Korean | `ell` | Greek | `ind` | Indonesian | | `spa` | Spanish | `tam` | Tamil | `dan` | Danish | | `deu` | German | `tgl` | Tagalog | `swe` | Swedish | | `fra` | French | `fin` | Finnish | `msa` | Malay | | `ita` | Italian | `zho` | Chinese | `ces` | Czech | | `pol` | Polish | `slk` | Slovak | `por` | Portuguese | | `nld` | Dutch | `ara` | Arabic | `bul` | Bulgarian | | `rus` | Russian | `hrv` | Croatian | `ron` | Romanian | | `ben` | Bengali | `hin` | Hindi | `hun` | Hungarian | | `nan` | Hokkien | `nor` | Norwegian | `pan` | Punjabi | | `tha` | Thai | `tur` | Turkish | `vie` | Vietnamese | | `yue` | Cantonese | | | | | If not specified, the language will be automatically detected from the input text. ## Error Handling The SDK uses Zig's error union for handling API errors: ```zig const response = client.textToSpeech(.{ .voice_id = "tc_672c5f5ce59fac2a48faeaee", .text = "Hello", .model = .ssfm_v30, }) catch |err| switch (err) { error.Unauthorized => { std.debug.print("Invalid API key\n", .{}); return err; }, error.PaymentRequired => { std.debug.print("Insufficient credits\n", .{}); return err; }, error.RateLimited => { std.debug.print("Rate limit exceeded - please retry later\n", .{}); return err; }, error.NotFound => { std.debug.print("Voice not found\n", .{}); return err; }, else => return err, }; defer allocator.free(response.audio_data); ``` ### Error Types | Error | Status Code | Description | |-------|-------------|-------------| | `error.BadRequest` | 400 | Invalid request parameters | | `error.Unauthorized` | 401 | Invalid or missing API key | | `error.PaymentRequired` | 402 | Insufficient credits | | `error.NotFound` | 404 | Resource not found | | `error.UnprocessableEntity` | 422 | Validation error | | `error.RateLimited` | 429 | Rate limit exceeded | | `error.InternalServerError` | 500 | Server error | | `error.JsonParseError` | - | JSON parsing error | ## API Reference ### Client Methods | Method | Description | |--------|-------------| | `init(allocator, config)` | Create client with configuration | | `deinit()` | Clean up client resources | | `textToSpeech(request)` | Convert text to speech audio | | `generateToFile(path, request)` | Generate speech and save it directly to a local file | | `textToSpeechStream(request, callback)` | Stream audio chunks via callback | | `cloneVoice(allocator, audio, filename, name, model)` | Create a custom voice via instant cloning | | `deleteVoice(voice_id)` | Delete a custom cloned voice | | `getMySubscription()` | Get subscription info | | `getVoices(model)` | Get available voices (V1) | | `getVoicesV2(filter)` | Get voices with metadata (V2) | | `getVoiceV2(voice_id, model)` | Get a specific voice | ## Control silence duration Requires **0.2.12 or later**. `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. Pass these output settings to the corresponding request's `output`. For streaming, use the streaming output type. ```zig const output = typecast.models.Output{ .remove_silence_ms = 300 }; const stream_output = typecast.models.OutputStream{ .remove_silence_ms = 300 }; ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # PHP The official PHP library for the [Typecast API](https://studio.typecast.ai/developers/api). Convert text to lifelike speech using AI-powered voices. Built with Guzzle 7 for reliable HTTP communication. Requires PHP 8.1+ and Composer. Typecast PHP SDK Typecast PHP SDK Source Code ## Installation Install via Composer: ```bash composer require neosapience/typecast-php:0.1.14 ``` Recommended PHP SDK release: **v0.1.14** on Packagist, pinned in the command above. Requires **PHP 8.1 or higher** and Composer. Check your version with `php -v`. ## Quick Start ```php textToSpeech(new TTSRequest( voiceId: 'tc_672c5f5ce59fac2a48faeaee', text: "Hello there! I'm your friendly text-to-speech agent.", model: 'ssfm-v30', )); // Save audio file file_put_contents('output.wav', $response->audioData); echo "Duration: {$response->duration}s, Format: {$response->format}\n"; ``` ## Features - **Multiple Voice Models**: Support for `ssfm-v30` (latest) and `ssfm-v21` AI voice models - **Multi-language Support**: 35+ languages including English, Korean, Spanish, Japanese, Chinese, and more - **Emotion Control**: Preset emotions (normal, happy, sad, angry, whisper, toneup, tonedown) or smart context-aware inference - **Audio Customization**: Control loudness (LUFS -70 to 0), pitch (-12 to +12 semitones), tempo (0.5x to 2.0x), and format (WAV/MP3) - **Voice Discovery**: V2 Voices API with filtering by model, gender, age, and use cases - **Instant Voice Cloning**: Upload a WAV/MP3 sample and create a custom voice ID - **Timestamp TTS**: Word- and character-level alignment data for subtitles, karaoke, and lip-sync - **Streaming**: Real-time chunked audio delivery for low-latency playback via callback - **Guzzle 7**: Industry-standard HTTP client with automatic retries and connection pooling - **Type Safety**: Typed properties and named arguments (PHP 8.1+) ## Voice Recommendations Use `recommendVoices` when you know the desired style but not the exact `voice_id`. ```php $voices = $client->recommendVoices( 'warm female voice for a product tutorial', count: 3, ); foreach ($voices as $voice) { echo "{$voice->voiceId} {$voice->voiceName} {$voice->score}\n"; } ``` Recommendation results contain only `voiceId`, `voiceName`, and `score`. Use `getVoiceV2` or `getVoicesV2` when you need detailed metadata such as supported models, emotions, gender, age, or use cases. ## Configuration Set your API key via environment variable or pass directly: ```bash Environment Variable export TYPECAST_API_KEY="your-api-key-here" ``` ```php From Environment use Neosapience\Typecast\TypecastClient; $client = new TypecastClient( apiKey: getenv('TYPECAST_API_KEY'), ); ``` ```php Direct Configuration use Neosapience\Typecast\TypecastClient; $client = new TypecastClient( apiKey: 'your-api-key-here', ); ``` When requests go through your own proxy, set `baseUrl` to the proxy endpoint and omit `apiKey`. The SDK will not send the `X-API-KEY` header for empty or missing keys. Requests to the default Typecast host still require an API key. ```php Proxy without API key $client = new TypecastClient( baseUrl: 'https://your-proxy.example.com', ); ``` ## Advanced Usage ### Emotion Control (ssfm-v30) ssfm-v30 offers two emotion control modes: **Preset** and **Smart**. Let the AI infer emotion from context: ```php use Neosapience\Typecast\Models\{TTSRequest, SmartPrompt}; $response = $client->textToSpeech(new TTSRequest( voiceId: 'tc_672c5f5ce59fac2a48faeaee', text: 'Everything is going to be okay.', model: 'ssfm-v30', prompt: new SmartPrompt( previousText: 'I just got the best news!', nextText: "I can't wait to celebrate!", ), )); ``` Explicitly set emotion with preset values: ```php use Neosapience\Typecast\Models\{TTSRequest, PresetPrompt}; $response = $client->textToSpeech(new TTSRequest( voiceId: 'tc_672c5f5ce59fac2a48faeaee', text: 'I am so excited to show you these features!', model: 'ssfm-v30', prompt: new PresetPrompt( emotionPreset: 'happy', emotionIntensity: 1.5, ), )); ``` ### Audio Customization Control loudness, pitch, tempo, and output format: ```php use Neosapience\Typecast\Models\{TTSRequest, Output}; $response = $client->textToSpeech(new TTSRequest( voiceId: 'tc_672c5f5ce59fac2a48faeaee', text: 'Customized audio output!', model: 'ssfm-v30', output: new Output( targetLufs: -14.0, audioPitch: 2, audioTempo: 1.2, audioFormat: 'mp3', ), seed: 42, )); file_put_contents('output.mp3', $response->audioData); ``` ### Generate audio to a file Use `generateToFile` when you want the SDK to synthesize speech and write the audio bytes directly to a local file. The model defaults to `ssfm-v30`, and `.mp3` / `.wav` extensions infer the output format when no output format is set. Browse available voice IDs on the [Voices](https://studio.typecast.ai/developers/api/voices) page. ```php $client->generateToFile( 'output.mp3', 'Hello from Typecast.', 'tc_672c5f5ce59fac2a48faeaee' // Find voice IDs at https://studio.typecast.ai/developers/api/voices ); ``` ### Text pauses Use text pause markup when you only need silent gaps inside one composed text segment. Put `<|5s|>`, `<|1s|>`, `<|0.3s|>`, or `<|0.34413s|>` directly in the text. The value is interpreted as seconds and must end with `s`. This keeps the pause expression visible in plain text without adding separate pause calls. ```php $audio = $client->composeSpeech() ->defaults(new ComposerSettings(voiceId: 'tc_672c5f5ce59fac2a48faeaee', model: 'ssfm-v30')) ->say('Hello<|5s|>Nice to meet you<|1s|>Today<|2s|>how does the weather feel?') ->generate(); ``` ### Multi-speaker composition Use the composer chaining API when one output file needs different voices or per-segment options such as pitch, tempo, prompt, or seed. The composer sends segments to `POST /v1/text-to-speech/compose`, which returns WAV or MP3 directly. Set silence removal explicitly on TTS segments; explicit pauses are preserved. ```php use Neosapience\Typecast\ComposerSettings; use Neosapience\Typecast\Models\Output; $audio = $client->composeSpeech() ->defaults(new ComposerSettings(voiceId: 'tc_672c5f5ce59fac2a48faeaee', model: 'ssfm-v30')) ->say('Hello there') ->pause(5.0) ->say('Nice to meet you', new ComposerSettings( voiceId: 'tc_60e5426de8b95f1d3000d7b5', output: new Output(audioPitch: 2) )) ->say('Today') ->pause(2.0) ->say('How does the weather feel?') ->generate(); file_put_contents('conversation.wav', $audio->audioData); ``` ### Voice Discovery (V2 API) List and filter available voices with enhanced metadata: ```php use Neosapience\Typecast\Models\VoicesV2Filter; // Get all voices $voices = $client->getVoicesV2(); // Filter by criteria $filtered = $client->getVoicesV2(new VoicesV2Filter( model: 'ssfm-v30', gender: 'female', age: 'young_adult', )); foreach ($voices as $voice) { echo "ID: {$voice->voiceId}, Name: {$voice->voiceName}\n"; echo "Gender: {$voice->gender}, Age: {$voice->age}\n"; } // Get a specific voice by ID $voice = $client->getVoiceV2('tc_672c5f5ce59fac2a48faeaee'); ``` ### Streaming Stream audio chunks in real-time for low-latency playback via callback: ```php use Neosapience\Typecast\Models\TTSRequestStream; $first = true; $client->textToSpeechStream( new TTSRequestStream( voiceId: 'tc_672c5f5ce59fac2a48faeaee', text: 'Stream this text as audio in real time.', model: 'ssfm-v30', ), function (string $chunk) use (&$first): void { if ($first) { $chunk = substr($chunk, 44); // Skip 44-byte WAV header $first = false; } // $chunk is raw 16-bit mono PCM at 32000 Hz // Feed to your audio output or pipe to ffplay }, ); ``` **WAV streaming format:** 32000 Hz, 16-bit, mono PCM. The first chunk includes a 44-byte WAV header (size = `0xFFFFFFFF`); subsequent chunks are raw PCM only. For MP3: 320 kbps, 44100 Hz, each chunk is independently decodable. ## Timestamp TTS `textToSpeechWithTimestamps()` wraps `POST /v1/text-to-speech/with-timestamps` and returns the audio together with per-word and per-character alignment data - useful for karaoke highlights, subtitle generation, and lip-sync applications. ### Basic Usage ```php use Neosapience\Typecast\TypecastClient; use Neosapience\Typecast\Models\TTSRequestWithTimestamps; $client = new TypecastClient(apiKey: 'YOUR_API_KEY'); $result = $client->textToSpeechWithTimestamps(new TTSRequestWithTimestamps( voiceId: 'tc_60e5426de8b95f1d3000d7b5', text: 'Hello. How are you?', model: 'ssfm-v30', )); file_put_contents('output.wav', $result->audioData); echo "Duration: {$result->audioDuration}s\n"; foreach ($result->words as $word) { echo " [{$word->startTime}s – {$word->endTime}s] {$word->text}\n"; } ``` ### Granularity Pass `granularity: 'word'` (default) or `granularity: 'char'` to control the alignment unit. ```php $result = $client->textToSpeechWithTimestamps(new TTSRequestWithTimestamps( voiceId: 'tc_60e5426de8b95f1d3000d7b5', text: 'Hello. How are you?', model: 'ssfm-v30', granularity: 'char', // required for Japanese / Chinese )); ``` ### Subtitle Export ```php file_put_contents('output.srt', $result->toSrt()); file_put_contents('output.vtt', $result->toVtt()); ``` **Japanese / Chinese:** Word-level segmentation is not meaningful for languages without whitespace delimiters (jpn, zho). Use `granularity: 'char'` for these languages to get character-level alignment. ## Instant Voice Cloning Clone a custom voice from a short audio sample, then pass the returned `uc_` voice ID directly to TTS. ```php cloneVoice( audio: file_get_contents('sample.wav'), filename: 'sample.wav', name: 'My Voice', model: 'ssfm-v30', ); $response = $client->textToSpeech(new TTSRequest( voiceId: $voice->voiceId, text: 'Hello from my cloned voice!', model: 'ssfm-v30', )); file_put_contents('output.wav', $response->audioData); $client->deleteVoice($voice->voiceId); ``` Voice cloning audio must be **25 MB or smaller**, the audio duration must be **5-150 seconds**, and the custom voice name must be **1-30 characters**. ## Supported Languages The SDK supports 35+ languages with automatic language detection: | Code | Language | Code | Language | Code | Language | |------|----------|------|----------|------|----------| | `eng` | English | `jpn` | Japanese | `ukr` | Ukrainian | | `kor` | Korean | `ell` | Greek | `ind` | Indonesian | | `spa` | Spanish | `tam` | Tamil | `dan` | Danish | | `deu` | German | `tgl` | Tagalog | `swe` | Swedish | | `fra` | French | `fin` | Finnish | `msa` | Malay | | `ita` | Italian | `zho` | Chinese | `ces` | Czech | | `pol` | Polish | `slk` | Slovak | `por` | Portuguese | | `nld` | Dutch | `ara` | Arabic | `bul` | Bulgarian | | `rus` | Russian | `hrv` | Croatian | `ron` | Romanian | | `ben` | Bengali | `hin` | Hindi | `hun` | Hungarian | | `nan` | Hokkien | `nor` | Norwegian | `pan` | Punjabi | | `tha` | Thai | `tur` | Turkish | `vie` | Vietnamese | | `yue` | Cantonese | | | | | If not specified, the language will be automatically detected from the input text. ## Error Handling The SDK throws specific exceptions for each HTTP error: ```php use Neosapience\Typecast\Exceptions\{ TypecastException, UnauthorizedException, PaymentRequiredException, RateLimitException, }; try { $response = $client->textToSpeech($request); } catch (UnauthorizedException $e) { echo "Invalid API key: {$e->getMessage()}\n"; } catch (PaymentRequiredException $e) { echo "Insufficient credits\n"; } catch (RateLimitException $e) { echo "Rate limit exceeded - please retry later\n"; } catch (TypecastException $e) { echo "Error: {$e->getMessage()}\n"; } ``` | Exception | Status Code | Description | |-----------|-------------|-------------| | `BadRequestException` | 400 | Invalid request parameters | | `UnauthorizedException` | 401 | Invalid or missing API key | | `PaymentRequiredException` | 402 | Insufficient credits | | `NotFoundException` | 404 | Resource not found | | `UnprocessableEntityException` | 422 | Validation error | | `RateLimitException` | 429 | Rate limit exceeded | | `InternalServerException` | 500 | Server error | ## API Reference ### TypecastClient Methods | Method | Description | |--------|-------------| | `textToSpeech(TTSRequest)` | Convert text to speech audio | | `generateToFile(path, text, voiceId)` | Generate speech and save it directly to a local file | | `textToSpeechStream(TTSRequestStream, callable)` | Stream audio chunks via callback | | `cloneVoice($audio, filename, name, model)` | Create a custom voice via instant cloning | | `deleteVoice(string $voiceId)` | Delete a custom cloned voice | | `getMySubscription()` | Get subscription info | | `getVoices(?string $model)` | Get available voices (V1) | | `getVoicesV2(?VoicesV2Filter)` | Get voices with metadata (V2) | | `getVoiceV2(string $voiceId)` | Get a specific voice | ## Control silence duration Requires **0.1.14 or later**. `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. Pass these output settings to the corresponding request's `output`. For streaming, use the streaming output type. ```php $output = new \Neosapience\Typecast\Models\Output(removeSilenceMs: 300); $streamOutput = new \Neosapience\Typecast\Models\OutputStream(removeSilenceMs: 300); ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Dart/Flutter The official Dart and Flutter SDK for the [Typecast API](https://studio.typecast.ai/developers/api). Convert text to lifelike speech, stream audio, generate timestamps, discover voices, and create custom voices from Dart or Flutter applications. Typecast Dart SDK Typecast Dart SDK Source Code ## Installation Install from pub.dev: ```bash dart pub add typecast_dart ``` Latest registered version: **0.1.13** on pub.dev. For Flutter projects: ```bash flutter pub add typecast_dart flutter pub add audioplayers ``` Use **typecast_dart 0.1.13 or higher**. For production Flutter apps, avoid embedding a long-lived API key in a distributed client. Route requests through your backend when the API key must remain private. ## Quick Start ```dart import 'package:audioplayers/audioplayers.dart'; import 'package:typecast_dart/typecast_dart.dart'; final client = TypecastClient(apiKey: 'YOUR_API_KEY'); final player = AudioPlayer(); Future speakAndPlay() async { final response = await client.textToSpeech( const TtsRequest( voiceId: 'tc_672c5f5ce59fac2a48faeaee', // Find voice IDs at https://studio.typecast.ai/developers/api/voices text: "Hello there! I'm your friendly text-to-speech agent.", model: TtsModel.ssfmV30, language: LanguageCode.eng, output: Output(audioFormat: AudioFormat.wav), ), ); await player.play(BytesSource(response.audioData)); print('Duration: ${response.duration}s, Format: ${response.format.value}'); } ``` ## Playback in Flutter The Dart SDK returns generated audio as bytes. In Flutter, pass those bytes to an audio playback package such as `audioplayers`. Use one shared `AudioPlayer` instance and play each response directly from memory: ```dart import 'package:audioplayers/audioplayers.dart'; import 'package:typecast_dart/typecast_dart.dart'; final client = TypecastClient(apiKey: 'YOUR_API_KEY'); final player = AudioPlayer(); Future playTts(String text) async { final response = await client.textToSpeech( TtsRequest( voiceId: 'tc_672c5f5ce59fac2a48faeaee', text: text, model: TtsModel.ssfmV30, language: LanguageCode.eng, output: const Output(audioFormat: AudioFormat.wav), ), ); await player.play(BytesSource(response.audioData)); } ``` For production Flutter apps, keep long-lived API keys on your backend. The Flutter app can request generated audio from your backend and still play the returned bytes with `BytesSource`. ## Features - **Multiple Voice Models**: Support for `ssfm-v30` and `ssfm-v21` AI voice models - **Multi-language Support**: 35+ languages including English, Korean, Japanese, Chinese, Spanish, and more - **Emotion Control**: Preset emotions or smart context-aware inference - **Audio Customization**: Control loudness, pitch, tempo, and output format - **Voice Discovery**: V2 Voices API with filtering by model, gender, age, and use cases - **Streaming**: Access the streaming TTS endpoint as a Dart `Stream>` - **Timestamp TTS**: Word- and character-level alignment data with SRT/VTT helpers - **Instant Voice Cloning**: Upload a WAV sample and create a custom voice ID - **Dart and Flutter**: Use the same package in Dart CLI, server, and Flutter projects ## Voice Recommendations Use `recommendVoices` when you know the desired style but not the exact `voice_id`. ```dart final voices = await client.recommendVoices( 'warm female voice for a product tutorial', count: 3, ); for (final voice in voices) { print('${voice.voiceId} ${voice.voiceName} ${voice.score}'); } ``` Recommendation results contain only `voiceId`, `voiceName`, and `score`. Use `getVoiceV2` or `getVoicesV2` when you need detailed metadata such as supported models, emotions, gender, age, or use cases. ## Configuration Set your API key via environment variable or constructor: ```bash Environment Variable export TYPECAST_API_KEY="your-api-key-here" ``` ```dart From Environment import 'dart:io'; import 'package:typecast_dart/typecast_dart.dart'; final client = TypecastClient( apiKey: Platform.environment['TYPECAST_API_KEY'], ); ``` ```dart Direct Configuration import 'package:typecast_dart/typecast_dart.dart'; final client = TypecastClient( apiKey: 'your-api-key-here', ); ``` When requests go through your own proxy, set `baseUrl` to the proxy endpoint and omit `apiKey`. The SDK will not send the `X-API-KEY` header for empty or missing keys. Requests to the default Typecast host still require an API key. ```dart Proxy without API key final client = TypecastClient( baseUrl: 'https://your-proxy.example.com', ); ``` ## Advanced Usage ### Emotion Control (ssfm-v30) ssfm-v30 offers two emotion control modes: **Preset** and **Smart**. Let the AI infer emotion from context: ```dart final response = await client.textToSpeech( const TtsRequest( voiceId: 'tc_672c5f5ce59fac2a48faeaee', text: 'Everything is going to be okay.', model: TtsModel.ssfmV30, prompt: SmartPrompt( previousText: 'I just got the best news!', nextText: "I can't wait to celebrate!", ), ), ); await player.play(BytesSource(response.audioData)); ``` Explicitly set emotion with preset values: ```dart final response = await client.textToSpeech( const TtsRequest( voiceId: 'tc_672c5f5ce59fac2a48faeaee', text: 'I am so excited to show you these features!', model: TtsModel.ssfmV30, prompt: PresetPrompt( emotionPreset: EmotionPreset.happy, emotionIntensity: 1.5, ), ), ); await player.play(BytesSource(response.audioData)); ``` ### Audio Customization Control loudness, pitch, tempo, and output format: ```dart final response = await client.textToSpeech( const TtsRequest( voiceId: 'tc_672c5f5ce59fac2a48faeaee', text: 'Customized audio output!', model: TtsModel.ssfmV30, output: Output( targetLufs: -14.0, audioPitch: 2, audioTempo: 1.2, audioFormat: AudioFormat.mp3, ), seed: 42, ), ); await player.play(BytesSource(response.audioData)); ``` ### Generate audio to a file Use `generateToFile` when you want the SDK to synthesize speech and write the audio bytes directly to a local file. The model defaults to `ssfm-v30`, and `.mp3` / `.wav` extensions infer the output format when no output format is set. Browse available voice IDs on the [Voices](https://studio.typecast.ai/developers/api/voices) page. ```dart await client.generateToFile( 'output.mp3', GenerateToFileRequest( text: 'Hello from Typecast.', voiceId: 'tc_672c5f5ce59fac2a48faeaee', // Find voice IDs at https://studio.typecast.ai/developers/api/voices ), ); ``` ### Text pauses Use text pause markup when you only need silent gaps inside one composed text segment. Put `<|5s|>`, `<|1s|>`, `<|0.3s|>`, or `<|0.34413s|>` directly in the text. The value is interpreted as seconds and must end with `s`. This keeps the pause expression visible in plain text without adding separate pause calls. ```dart final audio = await client .composeSpeech() .defaults(ComposerSettings(voiceId: 'tc_672c5f5ce59fac2a48faeaee', model: TTSModel.ssfmV30)) .say('Hello<|5s|>Nice to meet you<|1s|>Today<|2s|>how does the weather feel?') .generate(); ``` ### Multi-speaker composition Use the composer chaining API when one output file needs different voices or per-segment options such as pitch, tempo, prompt, or seed. The composer sends segments to `POST /v1/text-to-speech/compose`, which returns WAV or MP3 directly. Set silence removal explicitly on TTS segments; explicit pauses are preserved. ```dart final audio = await client .composeSpeech() .defaults(const ComposerSettings(voiceId: 'tc_672c5f5ce59fac2a48faeaee', model: TtsModel.ssfmV30)) .say('Hello there') .pause(5) .say( 'Nice to meet you', overrides: const ComposerSettings( voiceId: 'tc_60e5426de8b95f1d3000d7b5', output: Output(audioPitch: 2), ), ) .say('Today') .pause(2) .say('How does the weather feel?') .generate(); await File('conversation.wav').writeAsBytes(audio.audioData); ``` ### Voice Discovery (V2 API) List and filter available voices with enhanced metadata: ```dart final voices = await client.getVoicesV2(); final filtered = await client.getVoicesV2( const VoicesV2Filter( model: TtsModel.ssfmV30, gender: 'female', age: 'young_adult', ), ); for (final voice in voices) { print('ID: ${voice.voiceId}, Name: ${voice.voiceName}'); print('Gender: ${voice.gender}, Age: ${voice.age}'); print('Models: ${voice.models.map((model) => model.version).join(', ')}'); } final voice = await client.getVoiceV2('tc_672c5f5ce59fac2a48faeaee'); print(voice.voiceName); ``` ### Streaming Consume streaming audio as a Dart stream and play it without writing a file: ```dart import 'dart:typed_data'; final stream = await client.textToSpeechStream( const TtsRequestStream( voiceId: 'tc_672c5f5ce59fac2a48faeaee', text: 'Stream this text as audio in real time.', model: TtsModel.ssfmV30, output: OutputStream(audioFormat: AudioFormat.wav), ), ); final audioBytes = []; await for (final chunk in stream) { audioBytes.addAll(chunk); } await player.play(BytesSource(Uint8List.fromList(audioBytes))); ``` **WAV streaming format:** 32000 Hz, 16-bit, mono PCM. The first chunk includes a 44-byte WAV header (size = `0xFFFFFFFF`); subsequent chunks are raw PCM only. The example above avoids file storage and plays the complete stream from memory. For true low-latency chunk-by-chunk playback, feed the PCM chunks into a streaming audio engine instead of `audioplayers`. ## Timestamp TTS `textToSpeechWithTimestamps()` wraps `POST /v1/text-to-speech/with-timestamps` and returns audio together with word- or character-level alignment data. ```dart final result = await client.textToSpeechWithTimestamps( const TtsRequest( voiceId: 'tc_60e5426de8b95f1d3000d7b5', text: 'Hello. How are you?', model: TtsModel.ssfmV30, ), ); await player.play(BytesSource(result.audioBytes())); print('Duration: ${result.audioDuration}s'); for (final word in result.words) { print('[${word.startTime}s - ${word.endTime}s] ${word.word}'); } ``` ### Granularity Pass `granularity: 'word'` (default) or `granularity: 'char'` to control the alignment unit. ```dart final result = await client.textToSpeechWithTimestamps( const TtsRequest( voiceId: 'tc_60e5426de8b95f1d3000d7b5', text: 'Hello. How are you?', model: TtsModel.ssfmV30, ), granularity: 'char', ); ``` ### Subtitle Export ```dart await File('output.srt').writeAsString(result.toSrt()); await File('output.vtt').writeAsString(result.toVtt()); ``` ## Instant Voice Cloning Upload a short WAV sample to create a custom voice: ```dart final voice = await client.cloneVoice( audio: await File('sample.wav').readAsBytes(), filename: 'sample.wav', name: 'My Voice', model: TtsModel.ssfmV30, ); print('Custom voice ID: ${voice.voiceId}'); ``` Voice cloning audio must be **25 MB or smaller**, and the custom voice name must be **1-30 characters**. ## Control silence duration Requires **0.1.13 or later**. `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. Pass these output settings to the corresponding request's `output`. For streaming, use the streaming output type. ```dart final output = Output(removeSilenceMs: 300); final streamOutput = OutputStream(removeSilenceMs: 300); ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Ruby The official Ruby SDK for the [Typecast API](https://studio.typecast.ai/developers/api). Convert text to lifelike speech using AI-powered voices, generate timestamps, list voices, and create custom voices. The Ruby SDK uses only the Ruby standard library at runtime and supports Ruby 2.6+. Typecast Ruby SDK Typecast Ruby SDK Source Code ## Installation Install from RubyGems: ```bash gem install typecast-ruby ``` Latest registered version: **0.1.11** on RubyGems. Or add it to your Gemfile: ```ruby gem "typecast-ruby", "~> 0.1.11" ``` Requires **Ruby 2.6 or higher**. Check your version with `ruby -v`. ## Quick Start ```ruby require "typecast" client = Typecast::Client.new(api_key: ENV["TYPECAST_API_KEY"]) response = client.text_to_speech( Typecast::Models::TTSRequest.new( voice_id: "tc_672c5f5ce59fac2a48faeaee", text: "Hello there! I'm your friendly text-to-speech agent.", model: Typecast::Models::TTS_MODEL_V30, language: "eng", output: Typecast::Models::Output.new(audio_format: "wav") ) ) File.binwrite("output.wav", response.audio_data) puts "Duration: #{response.duration}s, Format: #{response.format}" ``` ## Features - **Multiple Voice Models**: Support for `ssfm-v30` and `ssfm-v21` AI voice models - **Multi-language Support**: 35+ languages including English, Korean, Japanese, Chinese, Spanish, and more - **Emotion Control**: Preset emotions or smart context-aware inference - **Audio Customization**: Control loudness, pitch, tempo, and output format - **Voice Discovery**: V2 Voices API with filtering by model, gender, age, and use cases - **Streaming Endpoint**: Access streaming TTS responses from Ruby - **Timestamp TTS**: Word- and character-level alignment data with SRT/VTT helpers - **Instant Voice Cloning**: Upload a WAV sample and create a custom voice ID - **No Runtime Dependencies**: Built on Ruby standard library `net/http` ## Voice Recommendations Use `recommend_voices` when you know the desired style but not the exact `voice_id`. ```ruby voices = client.recommend_voices( "warm female voice for a product tutorial", count: 3 ) voices.each do |voice| puts "#{voice.voice_id} #{voice.voice_name} #{voice.score}" end ``` Recommendation results contain only `voice_id`, `voice_name`, and `score`. Use `get_voice_v3` or `get_voices_v3` when you need current voice metadata. ## Configuration Set your API key via environment variable or constructor: ```bash Environment Variable export TYPECAST_API_KEY="your-api-key-here" ``` ```ruby From Environment require "typecast" client = Typecast::Client.new( api_key: ENV["TYPECAST_API_KEY"] ) ``` ```ruby Direct Configuration require "typecast" client = Typecast::Client.new( api_key: "your-api-key-here" ) ``` When requests go through your own proxy, set `base_url` to the proxy endpoint and omit `api_key`. The SDK will not send the `X-API-KEY` header for empty or missing keys. Requests to the default Typecast host still require an API key. ```ruby Proxy without API key client = Typecast::Client.new( base_url: "https://your-proxy.example.com" ) ``` You can also override the API host and HTTP timeouts: ```ruby client = Typecast::Client.new( api_key: ENV["TYPECAST_API_KEY"], base_url: "https://api.typecast.ai", open_timeout: 10, read_timeout: 30 ) ``` ## Advanced Usage ### Emotion Control (ssfm-v30) ssfm-v30 offers two emotion control modes: **Preset** and **Smart**. Let the AI infer emotion from context: ```ruby response = client.text_to_speech( Typecast::Models::TTSRequest.new( voice_id: "tc_672c5f5ce59fac2a48faeaee", text: "Everything is going to be okay.", model: Typecast::Models::TTS_MODEL_V30, prompt: Typecast::Models::SmartPrompt.new( previous_text: "I just got the best news!", next_text: "I can't wait to celebrate!" ) ) ) ``` Explicitly set emotion with preset values: ```ruby response = client.text_to_speech( Typecast::Models::TTSRequest.new( voice_id: "tc_672c5f5ce59fac2a48faeaee", text: "I am so excited to show you these features!", model: Typecast::Models::TTS_MODEL_V30, prompt: Typecast::Models::PresetPrompt.new( emotion_preset: "happy", emotion_intensity: 1.5 ) ) ) ``` ### Audio Customization Control loudness, pitch, tempo, and output format: ```ruby response = client.text_to_speech( Typecast::Models::TTSRequest.new( voice_id: "tc_672c5f5ce59fac2a48faeaee", text: "Customized audio output!", model: Typecast::Models::TTS_MODEL_V30, output: Typecast::Models::Output.new( target_lufs: -14.0, audio_pitch: 2, audio_tempo: 1.2, audio_format: Typecast::Models::AUDIO_MP3 ), seed: 42 ) ) File.binwrite("output.mp3", response.audio_data) ``` ### Generate audio to a file Use `generate_to_file` when you want the SDK to synthesize speech and write the audio bytes directly to a local file. The model defaults to `ssfm-v30`, and `.mp3` / `.wav` extensions infer the output format when no output format is set. Browse available voice IDs on the [Voices](https://studio.typecast.ai/developers/api/voices) page. ```ruby client.generate_to_file( 'output.mp3', text: 'Hello from Typecast.', voice_id: 'tc_672c5f5ce59fac2a48faeaee' # Find voice IDs at https://studio.typecast.ai/developers/api/voices ) ``` ### Text pauses Use text pause markup when you only need silent gaps inside one composed text segment. Put `<|5s|>`, `<|1s|>`, `<|0.3s|>`, or `<|0.34413s|>` directly in the text. The value is interpreted as seconds and must end with `s`. This keeps the pause expression visible in plain text without adding separate pause calls. ```ruby audio = client .compose_speech .defaults(voice_id: "tc_672c5f5ce59fac2a48faeaee", model: "ssfm-v30") .say("Hello<|5s|>Nice to meet you<|1s|>Today<|2s|>how does the weather feel?") .generate ``` ### Multi-speaker composition Use the composer chaining API when one output file needs different voices or per-segment options such as pitch, tempo, prompt, or seed. The composer sends segments to `POST /v1/text-to-speech/compose`, which returns WAV or MP3 directly. Set silence removal explicitly on TTS segments; explicit pauses are preserved. ```ruby audio = client .compose_speech .defaults(voice_id: "tc_672c5f5ce59fac2a48faeaee", model: Typecast::Models::TTS_MODEL_V30) .say("Hello there") .pause(5) .say("Nice to meet you", voice_id: "tc_60e5426de8b95f1d3000d7b5", output: { audio_pitch: 2 }) .say("Today") .pause(2) .say("How does the weather feel?") .generate File.binwrite("conversation.wav", audio.audio_data) ``` ### Voice Discovery (V3 API) List and filter available voices with enhanced metadata: ```ruby voices = client.get_voices_v3 filtered = client.get_voices_v3( Typecast::Models::VoicesV2Filter.new( model: Typecast::Models::TTS_MODEL_V30, gender: "female", age: "young_adult" ) ) voices.each do |voice| puts "ID: #{voice.voice_id}, Name: #{voice.voice_name.eng}" puts "Gender: #{voice.gender}, Age: #{voice.age}" end voice = client.get_voice_v3("tc_672c5f5ce59fac2a48faeaee") puts voice.voice_name.eng ``` ### Streaming Use `text_to_speech_stream()` to call the streaming endpoint: ```ruby client.text_to_speech_stream( Typecast::Models::TTSRequestStream.new( voice_id: "tc_672c5f5ce59fac2a48faeaee", text: "Stream this text as audio.", model: Typecast::Models::TTS_MODEL_V30, output: Typecast::Models::OutputStream.new(audio_format: "wav") ) ) do |audio| File.binwrite("stream.wav", audio) end ``` **WAV streaming format:** 32000 Hz, 16-bit, mono PCM. The first chunk includes a 44-byte WAV header (size = `0xFFFFFFFF`); subsequent chunks are raw PCM only. ## Timestamp TTS `text_to_speech_with_timestamps()` wraps `POST /v1/text-to-speech/with-timestamps` and returns audio together with word- or character-level alignment data. ```ruby result = client.text_to_speech_with_timestamps( Typecast::Models::TTSRequest.new( voice_id: "tc_60e5426de8b95f1d3000d7b5", text: "Hello. How are you?", model: Typecast::Models::TTS_MODEL_V30 ) ) result.save_audio("output.wav") puts "Duration: #{result.audio_duration}s" result.words.each do |word| puts "[#{word.start_time}s - #{word.end_time}s] #{word.word}" end ``` ### Granularity Pass `granularity: "word"` (default) or `granularity: "char"` to control the alignment unit. ```ruby result = client.text_to_speech_with_timestamps( Typecast::Models::TTSRequest.new( voice_id: "tc_60e5426de8b95f1d3000d7b5", text: "Hello. How are you?", model: Typecast::Models::TTS_MODEL_V30 ), granularity: "char" ) ``` ### Subtitle Export ```ruby File.write("output.srt", result.to_srt) File.write("output.vtt", result.to_vtt) ``` ## Instant Voice Cloning Upload a short WAV sample to create a custom voice: ```ruby voice = client.clone_voice( audio: File.binread("sample.wav"), filename: "sample.wav", name: "My Voice", model: Typecast::Models::TTS_MODEL_V30 ) puts "Custom voice ID: #{voice.voice_id}" ``` Voice cloning audio must be **25 MB or smaller**, and the custom voice name must be **1-30 characters**. ## Control silence duration Requires **0.1.11 or later**. `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. Pass these output settings to the corresponding request's `output`. For streaming, use the streaming output type. ```ruby output = Typecast::Models::Output.new(remove_silence_ms: 300) stream_output = Typecast::Models::OutputStream.new(remove_silence_ms: 300) ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Autotag SDK ## Overview **Typecast AutoTag** is a text preprocessing SDK that converts structured data (phone numbers, dates, times, amounts, etc.) into TTS-friendly formats for voice applications. @neosapience/typecast-autotag typecast-autotag com.neosapience:typecast-autotag Source, issues, and native binaries The Python package **typecast-autotag 2.0.0** requires Python 3.10 or later; the tested range is 3.10 through 3.13. **Python 3.8 and 3.9 are no longer supported because they have reached end of life (EOL).** The last Autotag Python package supporting them is **1.13.0**. Upgrade Python, then run `python -m pip install --upgrade typecast-autotag`. To temporarily keep an older environment, pin `python -m pip install "typecast-autotag==1.13.0"`; this does not restore EOL security support. This change applies to the Python package, independently of the JavaScript and Java package versions. ## Why AutoTag? When building voice applications, raw text often doesn't translate well to natural speech: | Input | Without AutoTag | With AutoTag | |-------|-----------------|--------------| | `555-123-4567` | "five five five dash one two three dash four five six seven" | "five five five one two three four five six seven" | | `$1,500` | "dollar-one-comma-five..." | "one thousand five hundred dollars" | | `14:30` | "fourteen-colon-thirty" | "two thirty PM" | AutoTag automatically detects these patterns and converts them to natural speech, improving the user experience in voice applications. ## Language Support The JavaScript and browser package accepts every **SSFM v3.0 TTS language**. | Language tier | Support | Official codes / accepted aliases | | --- | --- | --- | | Korean and English | Full patterns | `ko`, `kor`, `en`, `eng` | | Japanese and Simplified Chinese | Core TTS patterns | `ja`, `jpn`, `zh`, `zho` | | Traditional Han-script voices | Core TTS patterns | `zh-TW`, `nan`, `yue` | | Other SSFM v3.0 languages | Common TTS patterns (31) | Official ISO 639-3 codes below | Official SSFM v3.0 language codes (37): `ara`, `ben`, `bul`, `ces`, `dan`, `deu`, `ell`, `eng`, `fin`, `fra`, `hin`, `hrv`, `hun`, `ind`, `ita`, `jpn`, `kor`, `msa`, `nan`, `nld`, `nor`, `pan`, `pol`, `por`, `ron`, `rus`, `slk`, `spa`, `swe`, `tam`, `tgl`, `tha`, `tur`, `ukr`, `vie`, `yue`, `zho`. The five extra accepted values are aliases or a locale tag, not additional official languages: `ko` → `kor`, `en` → `eng`, `ja` → `jpn`, `zh` → `zho`, and `zh-TW` → Traditional Chinese. For the 31 languages without a dedicated rule module, AutoTag handles `datetime`, `date`, `time`, `money`, `phone`, `percentage`, `range`, `unit`, `serial`, and `number` patterns. It applies locale-specific date order, month and currency names, decimal separators, 12/24-hour conventions, and common native digit scripts. The `nan` and `yue` codes reuse the Traditional Chinese pattern pipeline while retaining their own TTS voice selection. Full support for English text preprocessing with proper number reading, currency formatting, and more. ```typescript import { autoTag } from '@neosapience/typecast-autotag'; autoTag('Call me at 555-123-4567.', { language: 'en' }); // → 'Call me at five five five one two three four five six seven.' autoTag('Total is $1,500.', { language: 'en' }); // → 'Total is one thousand five hundred dollars.' ``` Full support for Korean text preprocessing with natural number reading, date/time formatting, and more. Core Japanese TTS patterns, including irregular time and counter readings, contextual identifiers, and scripture references. ```typescript import { autoTag } from '@neosapience/typecast-autotag'; autoTag('受付は14時から19時まで、全部で6件です。', { language: 'ja' }); // → '受付はじゅうよじからじゅうくじまで、全部でろっけんです。' autoTag('注文番号はZX-407、ヨハネ3:16を確認してください。', { language: 'ja' }); // → '注文番号はZ・X、よん・ゼロ・なな、ヨハネさんしょうじゅうろくせつを確認してください。' ``` Core Simplified Chinese TTS patterns, including identifiers, flight numbers, units, and scripture references. ```typescript import { autoTag } from '@neosapience/typecast-autotag'; autoTag('订单编号是ZX-407,请读约翰福音3:16。', { language: 'zh' }); // → '订单编号是Z·X、四·零·七,请读约翰福音三章十六节。' ``` Traditional Chinese input with Taiwan phone, postal, currency, measurement, and identifier readings. ```typescript import { autoTag } from '@neosapience/typecast-autotag'; autoTag('客服時間是6–9點,費用是NT$12,800。', { language: 'zh-TW' }); // → '客服時間是六點到九點,費用是一萬二千八百新臺幣。' autoTag('訂單編號是ZX-407,請讀約翰福音3:16。', { language: 'zh-TW' }); // → '訂單編號是Z·X、四·零·七,請讀約翰福音三章十六節。' ``` Common patterns use the requested language's locale-specific number reading. ```typescript import { autoTag } from '@neosapience/typecast-autotag'; autoTag('Total 1,234.5 and 72.5%.', { language: 'spa' }); // → 'Total mil doscientos treinta y cuatro punto cinco and setenta y dos punto cinco%.' ``` All 37 official language codes are available in the JavaScript/TypeScript and browser package. Python, Java, and C/C++ currently expose Korean and English entry points. ## Installation Install the public npm package: ```bash pnpm add @neosapience/typecast-autotag # or npm install @neosapience/typecast-autotag yarn add @neosapience/typecast-autotag ``` ```typescript import { autoTag } from '@neosapience/typecast-autotag'; autoTag('Call me at 555-123-4567.', { language: 'en' }); ``` Install the public PyPI package: ```bash pip install typecast-autotag ``` ```python from typecast_autotag import auto_tag_en, manual_tag_en, auto_tag_with_manual_en result = auto_tag_en('Call 555-123-4567.') # → 'Call five five five, one two three, four five six seven.' ``` Add the Maven Central artifact to your project: ```xml com.neosapience typecast-autotag 1.13.0 ``` ```gradle implementation "com.neosapience:typecast-autotag:1.13.0" ``` ```java import ai.typecast.autotag.TypecastAutotag; String result = TypecastAutotag.autoTag("Call 555-123-4567.", "en"); // → "Call five five five, one two three, four five six seven." ``` Either grab a pre-built native binary from [GitHub Releases](https://github.com/neosapience/typecast-autotag/releases), or build from source: ```bash git clone https://github.com/neosapience/typecast-autotag.git cd typecast-autotag pnpm install pnpm c-binding:build-all-multiarch # Headers + libraries land under c-binding/build/ ``` ```c #include "typecast_autotag.h" char* result = typecast_auto_tag_english("Call 555-123-4567."); // → "Call five five five one two three four five six seven." typecast_free(result); ``` ## Quick Start ### Auto-Tagging Automatically detect and convert patterns in your text: ```typescript import { autoTag } from '@neosapience/typecast-autotag'; // Phone numbers autoTag('Call 555-123-4567', { language: 'en' }); // → 'Call five five five one two three four five six seven' // Dates and times autoTag('Meeting at 2:30 PM on January 15, 2024', { language: 'en' }); // → 'Meeting at two thirty PM on January fifteenth, twenty twenty-four' // Currency autoTag('Total: $1,234.56', { language: 'en' }); // → 'Total: one thousand two hundred thirty-four dollars and fifty-six cents' ``` ### Manual-Tagging Use explicit tag syntax for precise control: ```typescript import { manualTag } from '@neosapience/typecast-autotag'; // Read a verification code digit by digit manualTag('Your code is digits(2048).', { language: 'en' }); // → 'Your code is two zero four eight.' ``` ### Combined Usage Apply both auto and manual tags together: ```typescript import { autoTagWithManual } from '@neosapience/typecast-autotag'; autoTagWithManual('Code digits(2048), total $50.', { language: 'en' }); // → 'Code two zero four eight, total fifty dollars.' ``` Manual tags are processed first, then auto-tags are applied to the remaining text. ## Supported Tags Tag availability varies by language. Use `getSupportedAutoTags(language)` for the exact runtime list. Japanese, Simplified Chinese, and Taiwan Mandarin additionally recognize regional postal codes, ranges, scores, fractions, units, email symbols, directions, contextual serial/account/flight identifiers, and scripture references. ### Auto-Tags (Automatically Detected) | Tag | Description | Example | |-----|-------------|---------| | `phone` | Phone numbers | `555-123-4567` | | `datetime` | Date and time | `2024-01-15T14:30` | | `time` | Time | `2:30 PM` | | `date` | Date | `January 15, 2024` | | `money` | Currency | `$1,500` | | `year` | Year | `year 2024` | | `month` | Month | `January` | | `day` | Day | `the 15th` | | `order` | Ordinal | `1st place` | | `point` | Points/scores | `95 points` | | `ratio` | Ratio/percent | `50%`, `1:2` | | `weight` | Weight | `5kg`, `100lb` | | `distance` | Distance | `5km`, `100m` | | `temperature` | Temperature | `25°C`, `-5°F` | | `volume` | Volume | `500ml`, `2L` | | `dataCapacity` | Data size | `100GB`, `50Mbps` | ### Manual-Only Tags | Tag | Description | Syntax | Output | |-----|-------------|--------|--------| | `name` | Language-specific name handling | `name(김철수)` | `김 . 철 . 수` | | `digits` | Digit-by-digit | `digits(1234)` | `one two three four` | ## AICC Use Case Perfect for AI Contact Center applications where natural speech is critical: ```typescript import { autoTagWithManual } from '@neosapience/typecast-autotag'; // Customer service script const customerName = 'John Smith'; const orderNumber = '12345'; const deliveryDate = 'January 15, 2024'; const supportPhone = '1-800-555-1234'; const script = autoTagWithManual(` Hello, name(${customerName}). Your order number digits(${orderNumber}) will be delivered on ${deliveryDate}. For questions, please call ${supportPhone}. `, { language: 'en' }); // Output: // "Hello, John Smith. // Your order number one two three four five will be delivered on January fifteenth, twenty twenty-four. // For questions, please call one eight zero zero five five five one two three four." ``` ## Integration with Typecast TTS Combine AutoTag with Typecast TTS API for the best voice experience: ```typescript import { autoTagWithManual } from '@neosapience/typecast-autotag'; import { TypecastClient } from '@neosapience/typecast-js'; const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' }); // Preprocess text with AutoTag const rawText = 'Your balance is $1,234.56. Call 555-123-4567 for support.'; const processedText = autoTagWithManual(rawText, { language: 'en' }); // Send to Typecast TTS const audio = await client.textToSpeech({ text: processedText, model: 'ssfm-v30', voice_id: 'tc_672c5f5ce59fac2a48faeaee' }); ``` ## Platform Support ### Development Languages | Language | Version | Install path | Text languages | |----------|---------|--------------|----------------| | Node.js | ≥18 | `@neosapience/typecast-autotag` from npm | All 37 official codes + `ko`, `en`, `ja`, `zh`, `zh-TW` aliases | | Browser | Modern | `@neosapience/typecast-autotag` ESM/UMD bundle | All 37 official codes + `ko`, `en`, `ja`, `zh`, `zh-TW` aliases | | Python | ≥3.8 | `typecast-autotag` from PyPI | `ko`, `en` | | Java | ≥8 | `com.neosapience:typecast-autotag` from Maven Central | `ko`, `en` | | C/C++ | Any | Pre-built binary from Releases or `pnpm c-binding:build-all-multiarch` | `ko`, `en` | ### Server Platforms | Platform | Status | |----------|--------| | Linux | Supported (CentOS 6.9+, Amazon Linux 2+, Ubuntu, Debian) | | macOS | Supported (Intel & Apple Silicon) | | Windows | Supported (Windows 10+) | ### Architectures | Architecture | Status | |--------------|--------| | x86_64 (AMD64) | Supported | | x86 (32-bit) | Supported | | arm64 (AArch64) | Supported | | armv7 (32-bit ARM) | Supported | ## Next Steps Get started with Typecast TTS API Explore our SDK documentation --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Create shorts with AI Turn one video or image into a captioned vertical short with Typecast narration. Give the skill instructions to your AI agent, then review the script and preview. ## What you need A Typecast API account is all you need to get started. ## 1. Give the skill to your agent Open the section below, copy the complete skill text, and send it to your AI coding agent. The text is self-contained and does not require access to a separate repository. ````markdown --- name: create-typecast-shorts description: Create a captioned 9:16 short or reel from one rights-cleared local video or image using Typecast narration, timestamp output, and ffmpeg. Use when a user asks to make a short-form video, reel, vertical video, narrated social clip, or Typecast-powered short from media they own or are authorized to use. --- # Create Typecast Shorts Create one 1080×1920 MP4 from a local video or image, Typecast narration, and timestamp-aligned subtitles. ## Boundaries - Accept only an attached file or local file the user owns or is authorized to use. - If rights are unclear, ask the user to confirm them before processing. - Do not search for, download, scrape, or reuse third-party video, news, or social media. - Do not remove logos, watermarks, attribution, or embedded subtitles to conceal a source. - Do not upload or publish the result. Return local artifacts for user review. - Do not print, log, or place a Typecast API key in chat, commands, scripts, or output files. - Support one background video or image per run. Ask the user to choose one when several are provided. ## Workflow ### 1. Confirm inputs Collect: - Local media path - Topic or finished script - Language - Target duration; default to 45–60 seconds - Typecast voice ID, or permission to open the interactive voice picker - Output directory inside the current workspace Use these defaults unless the user specifies otherwise: - 1080×1920, 30 fps - Center crop to fill the frame - Discard source audio - White bottom-centered subtitles with a black outline Confirm media rights, the final script, and the voice before making the paid TTS request. ### 2. Check the environment Run: ```bash command -v cast command -v ffmpeg command -v ffprobe cast --help ffmpeg -filters 2>/dev/null | grep subtitles ``` If a command or the ffmpeg `subtitles` filter is missing, explain the missing dependency. Install it only with user approval. After installing cast with Go, add it to the current shell without hardcoding the user's home directory: ```bash export PATH="$(go env GOPATH)/bin:$PATH" ``` For cast installation, use the official options: ```bash brew install neosapience/tap/cast # or go install github.com/neosapience/cast@latest ``` Authenticate with `cast login` so the key is entered in its own prompt. Never ask the user to paste the key into chat. ### 3. Create a clean work directory Create a new, explicit directory inside the current workspace. Do not overwrite an existing output. Keep these artifacts: ```text script.txt script-tts.txt narration.wav captions.srt preview.mp4 final.mp4 ``` Work from this directory while rendering so `captions.srt` does not require platform-specific path escaping. ### 4. Prepare the script If the user supplied only a topic, draft a concise script with: 1. Hook 2. Main point 3. Supporting detail 4. Closing line Save the approved text as `script.txt`. Create `script-tts.txt` separately. Change only pronunciations that TTS may misread, such as numbers, abbreviations, URLs, symbols, or mixed-language terms. Preserve the meaning and never overwrite `script.txt`. ### 5. Select a voice and generate audio plus captions If no voice ID was provided, run: ```bash cast voices pick ``` After approval, generate narration and SRT together: ```bash cast "$(cat script-tts.txt)" \ --voice-id VOICE_ID \ --language LANGUAGE_CODE \ --format wav \ --out narration.wav \ --timestamp-out captions.srt \ --timestamp-format srt ``` Use ISO 639-3 language codes such as `kor`, `eng`, or `jpn`. For Japanese or Chinese, cast automatically selects character-level alignment; use the latest cast release if that behavior is unavailable. Do not fall back to Whisper merely to create timestamps. Typecast captions already returns aligned audio and subtitles without another model or dependency. ### 6. Inspect the source Run: ```bash ffprobe -v error -show_entries stream=codec_type,width,height,duration \ -of default=noprint_wrappers=1 "SOURCE_PATH" ``` Use the video command for a video source and the image command for a still image. Quote every user-provided path. ### 7. Render a 15-second preview For a video: ```bash ffmpeg -y -stream_loop -1 -i "SOURCE_PATH" -i narration.wav \ -filter_complex "[0:v]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,setsar=1,subtitles=captions.srt:force_style='FontSize=16,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,BorderStyle=1,Outline=1.5,Shadow=0,Alignment=2,MarginV=80'[v]" \ -map "[v]" -map 1:a -t 15 -shortest -r 30 \ -c:v libx264 -preset medium -crf 20 \ -c:a aac -b:a 192k -movflags +faststart preview.mp4 ``` For an image: ```bash ffmpeg -y -loop 1 -framerate 30 -i "SOURCE_PATH" -i narration.wav \ -filter_complex "[0:v]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,setsar=1,subtitles=captions.srt:force_style='FontSize=16,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,BorderStyle=1,Outline=1.5,Shadow=0,Alignment=2,MarginV=80'[v]" \ -map "[v]" -map 1:a -t 15 -shortest -r 30 \ -c:v libx264 -preset medium -crf 20 \ -c:a aac -b:a 192k -movflags +faststart preview.mp4 ``` Show the preview to the user. Check framing, subtitle readability, pronunciation, and timing before the full render. If center crop cuts off important content, replace the scale and crop portion with: ```text scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black ``` ### 8. Render and verify the final video After preview approval, rerun the matching command without `-t 15`, add `-shortest`, and write `final.mp4`. Verify: ```bash ffprobe -v error \ -show_entries stream=codec_name,width,height -show_entries format=duration,size \ -of default=noprint_wrappers=1 final.mp4 ``` Require: - 1080×1920 video - H.264 video and AAC audio - Non-zero duration and size - Narration and subtitles ending with the video Return the output directory and artifact list. Remind the user to review the complete video before publishing it themselves. ## Failure handling - For 401 or 403 from cast, re-run `cast login` and verify the Global API plan without exposing the key. - For 402 or 429, report the billing or rate-limit response; do not retry repeatedly. - If timestamp flags are unavailable, update cast instead of adding a parallel transcription stack. - If ffmpeg cannot load subtitles, use an ffmpeg build with libass. - If rendering fails, preserve all existing artifacts and rerun only the failed step. ```` ## 2. Create it through conversation Describe the short you want, then change any of these details as you talk with your agent. | What you can change | Try saying | | -------------------- | ---------------------------------------------------------------------------- | | Script and message | “Open with a stronger hook” or “focus on one key idea.” | | Voice and tone | “Try a brighter, more energetic voice” or “read it more calmly.” | | Length and pacing | “Keep it under 30 seconds” or “make the pacing a little faster.” | | Captions and visuals | “Split the captions into shorter lines” or “keep the main subject in frame.” | Keep talking with your agent and refine each part until the short feels right. ## 3. Review and approve The agent asks you to approve: 1. The script and Typecast voice 2. A 15-second preview with narration and captions 3. The final render After approval, you receive a 1080×1920 MP4 along with the script, narration, and SRT caption files. When uploading media, clearly identify its copyright source. ## What the agent handles Checks and installs the cast CLI and ffmpeg after asking for approval. Prepares the script and generates Typecast narration securely. Creates timestamp-aligned SRT captions from the same narration. Renders a preview and final 9:16 MP4 for your review. You only need to provide the media, topic, and approvals. Let the agent handle the command-line tools and rendering steps. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Community integrations Community projects connect Typecast TTS to short-form video, dubbing, narration, audio asset production, agent frameworks, and developer tools. These integrations are built and maintained by third-party developers. These projects are not maintained or endorsed by Typecast. Review each repository's documentation, license, security practices, and current API compatibility before using it in production. Links were last reviewed on August 14, 2026. ## Content and video production Start here if you are building short-form video, narration, subtitles, character audio, or automated media workflows. Automate blog publishing and repurpose posts into short-form videos, with Typecast narration and Whisper-derived word timestamps. Connect Typecast voices, emotions, and loudness controls to AI video generation and CapCut, Premiere, and Vrew export workflows. Build scenes, captions, multi-voice narration, and CapCut drafts from a script using timestamped TTS. Generate per-cut MP3 files and SRT subtitles from a narration script with a focused command-line workflow. Add Typecast narration to automated Instagram, TikTok, and Pinterest affiliate video production. Use an MCP helper to list voices, generate timestamped speech, and save audio inside a video production monorepo. Turn uploaded photos into blog posts and short-form videos, using Typecast narration fitted to each video scene. Batch-generate character reaction audio from a YAML manifest with a small Typecast-focused tool. ## Agents and real-time voice These projects show Typecast inside conversational agents, real-time speech engines, compatibility layers, and evaluation tools. Add speech generation, voice listing, and voice detail tools to a LlamaIndex agent. Use streaming Typecast TTS in conversational, real-time voice agents built with TEN Framework. Use Typecast as a Python real-time TTS engine with voice lookup, emotion controls, and PyAudio streaming. Explore a local voice assistant that connects speech recognition, an LLM, and Typecast speech synthesis. Route OpenAI-compatible TTS clients to Typecast through a multi-provider backend. This repository is archived and no longer maintained. Compare Typecast SSFM 3.0 with other speech models in a crowdsourced blind evaluation platform. Build a multimodal interactive robot with vision, memory, and Gemini dialogue, using Typecast as an optional asynchronous speech engine. Generate and play Typecast speech in Discord with a focused JavaScript bot example. ## Before you use a project * Confirm that it uses the current Typecast API or an actively maintained Typecast SDK. * Keep API keys on the server or in environment variables. Never commit credentials to a repository. * Check the project's license before copying or distributing its code. Some projects in this list do not declare a license. * Pin compatible versions and test voice, model, streaming, and timestamp behavior in your own environment. Built something useful with Typecast TTS? Share a public repository with a clear setup guide, license, and Typecast integration path so the community can evaluate and reuse it. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # OpenClaw [OpenClaw](https://docs.openclaw.ai/) is an AI agent runtime that can execute tools, shell commands, and MCP servers. With Typecast, your agent can generate natural-sounding speech in a single command. ## What You Can Do With Typecast and OpenClaw, you can: - **Generate speech from agent workflows** - Convert any text output to natural-sounding audio - **Choose from 600+ voices** - Select voices by gender, age, and style - **Control emotion** - Apply Smart Emotion or preset emotions (happy, sad, angry, whisper, etc.) - **Support 37 languages** - Generate speech in English, Korean, Japanese, Chinese, and more - **Automate audio pipelines** - Combine with other tools for end-to-end content creation --- ## Prerequisites Before you start, make sure you have: 1. **OpenClaw** installed - `npm install -g openclaw@latest` 2. **Typecast API Key** - [Get yours here](https://studio.typecast.ai/developers/api/) 3. **Typecast CLI (`cast`)** - The fastest integration path --- ## Quick Start: cast CLI The official Typecast CLI turns speech generation into a single shell command. If your agent can run shell commands, it can generate Typecast audio without writing a custom provider. ### Step 1: Install the CLI ```bash brew install neosapience/tap/cast ``` ```bash go install github.com/neosapience/cast@latest ``` ### Step 2: Authenticate ```bash cast login ``` Or pass the key directly: ```bash cast login ``` ### Step 3: Verify ```bash cast "Hello, world!" --out ./test.mp3 --format mp3 ``` If the file is generated successfully, you're ready to use it with OpenClaw. --- ## Integration Methods ### Method 1: cast via Local exec (Recommended) OpenClaw distinguishes local `exec` from remote `code_execution`. Use local `exec` when the command must access installed binaries on the machine. Simply ask your OpenClaw agent: ```text Use local exec to run: cast "Your reservation has been confirmed for Friday at 7 PM." --language eng --format mp3 --out ./confirmation.mp3 Return the generated file path. ``` For repeated use, add a project instruction to your OpenClaw config: ```markdown When the user asks for spoken audio, use the local `cast` CLI. Default command: cast "$TEXT" --voice-id "$TYPECAST_VOICE_ID" --language "${TYPECAST_LANGUAGE:-eng}" --format "${TYPECAST_FORMAT:-mp3}" --out "$OUTPUT" Never print API keys. Prefer `--out` for headless sessions. ``` **Recommended environment variables:** ```bash export TYPECAST_VOICE_ID="tc_60e5426de8b95f1d3000d7b5" export TYPECAST_LANGUAGE="eng" export TYPECAST_FORMAT="mp3" ``` ### Method 2: MCP Server (Tool-Native) For deeper integration, connect the Typecast API MCP server so OpenClaw can call TTS tools directly. ```bash openclaw mcp set typecast '{ "command": "uvx", "args": [ "--from", "git+https://github.com/neosapience/typecast-api-mcp-server.git", "typecast-api-mcp-server" ], "env": { "TYPECAST_API_KEY": "${TYPECAST_API_KEY}", "TYPECAST_OUTPUT_DIR": "./typecast_output" } }' ``` Verify: ```bash openclaw mcp show typecast ``` Add to your OpenClaw plugin bundle: ```json { "mcp": { "servers": { "typecast": { "command": "uvx", "args": [ "--from", "git+https://github.com/neosapience/typecast-api-mcp-server.git", "typecast-api-mcp-server" ], "env": { "TYPECAST_API_KEY": "${TYPECAST_API_KEY}", "TYPECAST_OUTPUT_DIR": "./typecast_output" }, "connectionTimeoutMs": 30000 } } } } ``` Once registered, Typecast tools appear as `typecast__synthesize_speech`, `typecast__list_voices`, etc. Ask your agent: ```text Use the typecast MCP tools to synthesize "Hello from Typecast" as an mp3 file. ``` You can also connect the remote docs MCP at `https://typecast.ai/docs/mcp` for integration guidance - it provides Typecast documentation as MCP resources without generating audio. --- ## Voice and Emotion Control ### Finding Voices Use the `cast` CLI to list available voices: ```bash cast voices --model ssfm-v30 ``` Or ask your agent to use the MCP `list_voices` tool to browse by gender, age, and use case. ### Emotion Options AI automatically detects the best emotion from text context. Great for natural conversations and storytelling. Manually choose from 7 emotions: Normal, Happy, Sad, Angry, Whisper, Tone Up, Tone Down. **cast CLI with emotion:** ```bash # Smart Emotion (ssfm-v30 only) cast "I can't believe we won!" --model ssfm-v30 --emotion smart --out ./excited.mp3 # Preset Emotion cast "I'm sorry to hear that." --model ssfm-v30 --emotion sad --out ./sorry.mp3 ``` --- ## Example Workflows 1. OpenClaw receives meeting transcript 2. Agent summarizes key points with an LLM 3. Agent runs `cast` to generate audio summary 4. Output file is uploaded to Slack or Google Drive 1. Agent receives content in English 2. Translates to Korean, Japanese, Chinese 3. Generates Typecast audio for each language 4. Saves all audio files to cloud storage 1. Build pipeline triggers OpenClaw agent 2. Agent generates status message: "Build succeeded" or "Build failed" 3. `cast` produces audio notification 4. Audio is posted to team Discord channel --- ## Troubleshooting Install the CLI in the same runtime where OpenClaw executes tools. If installed via Homebrew, verify your `PATH` includes the Homebrew bin directory. Run `cast login` or pass your API key directly with `cast login `. Verify at the [Typecast API Console](https://studio.typecast.ai/developers/api/). Use `--out` to save to a file instead of playing audio. Return the file path to the user. Explicitly ask the agent to use the `exec` tool or local shell. Add a project instruction to clarify this behavior. - Ensure `uvx` is installed and on `PATH`: `command -v uvx` - Check that `TYPECAST_API_KEY` is set in the environment - Run `openclaw mcp show typecast` to verify registration --- ## Resources Get your API key Browse all available voices Explore the Typecast API Typecast MCP Server docs --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Skills [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview) is Anthropic's AI coding assistant. With **Typecast Skills**, Claude can help you integrate TTS into your projects - just ask in natural language! ## What You Can Do With Typecast Skills for Claude, you can: - **Get step-by-step guidance** for API integration in Python, JavaScript, or cURL - **Generate working code** tailored to your use case - **Troubleshoot errors** with detailed explanations and solutions - **Compare pricing plans** and calculate costs - **Discover voices** that match your project needs --- ## Prerequisites Before you start, make sure you have: 1. **Claude Code** - Install from [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview) (official installation guide) 2. **Typecast API Key** - [Get yours here](https://studio.typecast.ai/developers/api/) --- ## Installation Open Claude Code and run the following command: ```bash npx skills add neosapience/typecast-skills ``` That's it! You're ready to use Typecast Skills. ### Verify Installation Ask Claude: **"What can you help me with for Typecast API?"** If installed correctly, Claude will introduce itself as the Typecast TTS API expert agent. **Manual Installation**: You can also copy the skill folder directly to: - Personal scope: `~/.claude/skills/` - Project scope: `.cursor/skills/` --- ## Quick Start: Your First Voice Generation Just ask Claude in natural language! Here are some example prompts: ### Basic Setup ```text Get Started How do I get started with Typecast TTS API? ``` ```text API Key How do I get a Typecast API key? ``` ```text Voice List Show me how to list available voices in Python. ``` ### Generate Code ```text Python Write Python code to convert "Hello world" to speech using Typecast API. ``` ```text JavaScript Create a JavaScript function for text-to-speech with happy emotion. ``` ```text cURL Give me a cURL command to generate speech with the ssfm-v30 model. ``` ### Troubleshoot Errors ```text 403 Error I'm getting a 403 error with Typecast API. What should I check? ``` ```text Rate Limit I got error 429 - too many requests. What are the rate limits? ``` ```text Voice Not Found My voice_id returns 404. How do I find valid voice IDs? ``` --- ## Example Conversations ### Getting Started (No Coding Experience) **Claude will:** 1. Explain what an API is in simple terms 2. Guide you through getting an API key 3. Show the simplest possible code example 4. Explain each line of code **Claude will:** - Explain that v30 is the latest model with better quality - List the available emotions for each model - Recommend v30 for new projects - Explain Smart Mode (context-aware emotion) ### Integrating with Your Project **Claude will:** 1. Explain that API calls should go through a backend (security) 2. Provide a Node.js/Express backend example 3. Show how to call it from React 4. Include error handling **Claude will:** 1. Generate a complete FastAPI endpoint 2. Use environment variables for the API key 3. Include proper error handling 4. Show example request/response --- ## What Claude Knows About Typecast | Topic | What Claude Can Help With | |-------|---------------------------| | **API Basics** | Authentication, endpoints, request/response format | | **Code Samples** | Python SDK, JavaScript SDK, Direct API, cURL | | **Voice Selection** | 600+ voices, filtering by gender/age/use case | | **Emotion Control** | Preset emotions, Smart Mode, intensity adjustment | | **Audio Settings** | Format (WAV/MP3), volume, pitch, tempo | | **Error Handling** | All error codes (400-500) with solutions | | **Pricing** | Plan comparison, credit calculation | | **Best Practices** | Security, environment variables, rate limits | --- ## Tips for Best Results Instead of "help with TTS", try "generate Python code for sad emotion with ssfm-v30" Tell Claude your tech stack: "I'm using Next.js with TypeScript" Paste the full error message - Claude will diagnose and fix it Claude remembers context, so ask "now add error handling" or "convert to TypeScript" --- ## Keeping Up to Date Update Typecast Skills to get the latest features and fixes: ```bash npx skills update ``` --- ## Troubleshooting - Verify the skill is properly installed in your settings - Try restarting Claude Code - Check that the skills repository is accessible - Ask Claude to check the latest documentation - Mention you want "ssfm-v30" specifically for the latest features - Update your local skills repository if cloned locally - Make sure you've replaced `YOUR_API_KEY` with your actual key - Check that you're using a valid `voice_id` - Verify your API plan supports the model you're using --- ## Resources Get your Typecast API key Browse all available voices Explore the Typecast API View source on GitHub --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Google Sheets [Google Sheets](https://sheets.google.com/) combined with [Apps Script](https://script.google.com/) lets you automate text-to-speech generation right from your spreadsheet. Perfect for batch processing, content automation, and team workflows! ## What You Can Do With Typecast and Google Sheets, you can: - **Batch generate TTS** - Convert multiple texts to speech with one click - **Automate workflows** - Process data and generate audio automatically - **No coding required** - Use the simple custom menu interface - **Team collaboration** - Share sheets with team members for collaborative audio production - **Auto-save to Drive** - Generated audio files are automatically stored in Google Drive --- ## Prerequisites Before you start, make sure you have: 1. **Google Account** - Access to Google Sheets 2. **Typecast API Key** - [Get yours here](https://studio.typecast.ai/developers/api/) --- ## Setup Guide ### Step 1: Create Your Spreadsheet 1. Open [Google Sheets](https://sheets.google.com/) 2. Create a new blank spreadsheet 3. Set up your columns with headers in the **first row**: - **Column A**: `Text` - The text you want to convert to speech - **Column B**: `Voice` - Voice ID (e.g., `tc_66aca22c7d31e45ff05ff418`) or Voice Name from the [Voice Library](https://studio.typecast.ai/developers/api/voices) - **Column C**: `Language` - Language code (`eng`, `kor`, `jpn`, `cmn`, etc.) - **Column D**: `Audio URL` - Leave empty (will be auto-filled) 4. Add your data starting from the **second row** (row 1 is reserved for headers) Google Sheet with Text, Voice, Language, and Audio URL columns You can use **voice names** (like `Arin`) or **voice IDs** (like `tc_66aca22c7d31e45ff05ff418`)! The script automatically looks up voice IDs from names. Find available voices and their names at **[Voice Library](https://studio.typecast.ai/developers/api/voices)** or using the [Voices API](https://studio.typecast.ai/developers/api/voices). The script uses **Smart Emotion** and **ssfm-v30 model** by default for natural-sounding speech! ### Step 2: Open Apps Script Editor 1. Click **Extensions** in the menu bar 2. Select **Apps Script** Extensions menu showing Apps Script option 3. A new tab will open with the Apps Script editor Apps Script code editor interface ### Step 3: Add the Typecast Integration Code **IMPORTANT: API Key Required!** Before using the script, you MUST replace `YOUR_API_KEY_HERE` on line 2 with your actual Typecast API key. Get your API key from the [Typecast API console](https://studio.typecast.ai/developers/api/). 1. Delete the default `myFunction()` code 2. Copy and paste the following code: ```javascript // Typecast API Configuration const TYPECAST_API_KEY = "YOUR_API_KEY_HERE"; // Replace with your actual API key const TYPECAST_API_URL = "https://api.typecast.ai/v1/text-to-speech"; /** * Creates custom menu when spreadsheet opens */ function onOpen() { const ui = SpreadsheetApp.getUi(); ui.createMenu("🎙️ Typecast TTS") .addItem("Generate All Audio", "generateAllAudio") .addItem("Generate Selected Rows", "generateSelectedAudio") .addSeparator() .addItem("Clear Audio URLs", "clearAudioUrls") .addToUi(); } /** * Generates audio for all rows with text */ function generateAllAudio() { const sheet = SpreadsheetApp.getActiveSheet(); const lastRow = sheet.getLastRow(); if (lastRow < 2) { SpreadsheetApp.getUi().alert("No data to process!"); return; } // Get all data at once for better performance const dataRange = sheet.getRange(2, 1, lastRow - 1, 4); const data = dataRange.getValues(); let successCount = 0; let errorCount = 0; // Process each row data.forEach((row, index) => { const text = row[0]; const voiceNameOrId = row[1]; const language = row[2] || "eng"; // Default to English if not specified const rowNumber = index + 2; // Skip if text or voice name/ID is empty if (!text || !voiceNameOrId) { return; } // Skip if audio URL already exists if (row[3]) { return; } try { const audioUrl = callTypecastAPI(text, voiceNameOrId, language); sheet.getRange(rowNumber, 4).setValue(audioUrl); successCount++; // Add a small delay to avoid rate limiting Utilities.sleep(500); } catch (error) { sheet.getRange(rowNumber, 4).setValue("Error: " + error.message); errorCount++; } }); SpreadsheetApp.getUi().alert( `Generation complete!\n\nSuccess: ${successCount}\nErrors: ${errorCount}`, ); } /** * Generates audio for selected rows only */ function generateSelectedAudio() { const sheet = SpreadsheetApp.getActiveSheet(); const selection = sheet.getActiveRange(); const startRow = selection.getRow(); const numRows = selection.getNumRows(); if (startRow === 1) { SpreadsheetApp.getUi().alert("Please select data rows (not the header)"); return; } let successCount = 0; let errorCount = 0; for (let i = 0; i < numRows; i++) { const rowNumber = startRow + i; const text = sheet.getRange(rowNumber, 1).getValue(); const voiceNameOrId = sheet.getRange(rowNumber, 2).getValue(); const language = sheet.getRange(rowNumber, 3).getValue() || "eng"; if (!text || !voiceNameOrId) { continue; } try { const audioUrl = callTypecastAPI(text, voiceNameOrId, language); sheet.getRange(rowNumber, 4).setValue(audioUrl); successCount++; Utilities.sleep(500); } catch (error) { sheet.getRange(rowNumber, 4).setValue("Error: " + error.message); errorCount++; } } SpreadsheetApp.getUi().alert( `Generation complete!\n\nSuccess: ${successCount}\nErrors: ${errorCount}`, ); } /** * Clears all audio URLs from column D */ function clearAudioUrls() { const sheet = SpreadsheetApp.getActiveSheet(); const lastRow = sheet.getLastRow(); if (lastRow < 2) { return; } const response = SpreadsheetApp.getUi().alert( "Clear Audio URLs", "Are you sure you want to clear all audio URLs?", SpreadsheetApp.getUi().ButtonSet.YES_NO, ); if (response === SpreadsheetApp.getUi().Button.YES) { sheet.getRange(2, 4, lastRow - 1, 1).clearContent(); SpreadsheetApp.getUi().alert("Audio URLs cleared!"); } } /** * Gets voice ID from voice name by calling the Voices API * @param {string} voiceName - Voice name to look up * @returns {string} Voice ID (e.g., tc_66aca22c7d31e45ff05ff418) */ function getVoiceIdByName(voiceName) { const url = "https://api.typecast.ai/v2/voices"; const options = { method: "get", headers: { "X-API-KEY": TYPECAST_API_KEY, }, muteHttpExceptions: true, }; const response = UrlFetchApp.fetch(url, options); const responseCode = response.getResponseCode(); if (responseCode !== 200) { throw new Error(`Failed to fetch voices: ${responseCode}`); } const voices = JSON.parse(response.getContentText()); // Search for voice by name (case-insensitive) const searchName = voiceName.toLowerCase().trim(); const voice = voices.find((v) => v.voice_name.toLowerCase() === searchName); if (!voice) { throw new Error( `Voice "${voiceName}" not found. Please check the voice name or use voice ID instead.`, ); } return voice.voice_id; } /** * Calls the Typecast API to generate speech * @param {string} text - Text to convert to speech * @param {string} voiceNameOrId - Voice name (e.g., "Emily") or Voice ID (e.g., "tc_66aca22c7d31e45ff05ff418") * @param {string} language - Language code (eng, kor, jpn, cmn, etc.) * @returns {string} URL to the generated audio file */ function callTypecastAPI(text, voiceNameOrId, language) { // Trim and validate inputs text = String(text).trim(); voiceNameOrId = String(voiceNameOrId).trim(); language = String(language || "eng").trim(); if (!text || !voiceNameOrId) { throw new Error("Text and Voice Name/ID are required"); } // Determine if input is voice ID or name // Voice IDs start with "tc_" let voiceId; if (voiceNameOrId.startsWith("tc_")) { voiceId = voiceNameOrId; Logger.log("Using voice ID: " + voiceId); } else { // It's a voice name, look up the ID Logger.log("Looking up voice name: " + voiceNameOrId); voiceId = getVoiceIdByName(voiceNameOrId); Logger.log("Found voice ID: " + voiceId); } // Log for debugging Logger.log("Calling API with text: " + text); Logger.log("Language: " + language); const payload = { voice_id: voiceId, text: text, model: "ssfm-v30", language: language, prompt: { emotion_type: "smart", // Enable Smart Emotion for natural-sounding speech }, output: { audio_format: "mp3", }, }; // Log payload for debugging Logger.log("Payload: " + JSON.stringify(payload)); const options = { method: "post", contentType: "application/json", headers: { "X-API-KEY": TYPECAST_API_KEY, }, payload: JSON.stringify(payload), muteHttpExceptions: true, }; const response = UrlFetchApp.fetch(TYPECAST_API_URL, options); const responseCode = response.getResponseCode(); if (responseCode !== 200) { const errorBody = response.getContentText(); Logger.log("Error response: " + errorBody); throw new Error(`API Error: ${responseCode} - ${errorBody}`); } // Get the audio blob const audioBlob = response.getBlob(); // Upload to Google Drive const folder = DriveApp.getRootFolder(); // Or specify a folder const fileName = `typecast_${new Date().getTime()}.mp3`; const file = folder.createFile(audioBlob.setName(fileName)); // Set file to be accessible with link file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW); // Return the file URL return file.getUrl(); } ``` Complete Apps Script code in the editor **🔑 Don't Forget Your API Key!** Before running the script, you MUST update line 2: **Replace this:** ```javascript const TYPECAST_API_KEY = 'YOUR_API_KEY_HERE'; ``` **With your actual API key:** ```javascript const TYPECAST_API_KEY = 'your_actual_api_key_from_typecast'; ``` Get your API key from the [Typecast API console](https://studio.typecast.ai/developers/api/). 3. Click the **Save** icon (💾) or press `⌘+S` (Mac) / `Ctrl+S` (Windows) 4. Give your project a name (e.g., "Typecast TTS Integration") ### Step 4: Authorize the Script The first time you run the script, Google will ask for permissions: 1. Close the Apps Script tab and return to your spreadsheet 2. Refresh the page (F5 or `⌘+R`) 3. You should see a new menu: **🎙️ Typecast TTS** appear in the menu bar Custom menu showing Generate All Audio, Generate Selected Rows, and Clear Audio URLs options 4. Click **🎙️ Typecast TTS** → **Generate All Audio** Typecast TTS menu opened showing all available options 5. Google will prompt you to authorize the script: - Click **Continue** - Select your Google account - Click **Advanced** → **Go to [Project Name] (unsafe)** - Click **Allow** The "unsafe" warning appears because this is a custom script. It's safe to proceed if you trust the code you copied. --- ## Usage ### Generate Audio for All Rows 1. Click **🎙️ Typecast TTS** → **Generate All Audio** 2. The script will process all rows with text and voice ID Running script notification showing the generation in progress 3. When complete, you'll see a success message Success dialog showing 3 audio files generated with 0 errors 4. Audio URLs will appear in Column D 5. Generated audio files are saved to your Google Drive **Smart Emotion is enabled by default!** The script uses `emotion_type: 'smart'` with the `ssfm-v30` model for natural-sounding, emotionally appropriate speech. Spreadsheet showing generated Google Drive URLs in Column D with Smart Emotion enabled ### Generate Audio for Selected Rows 1. Select the rows you want to process (click and drag on row numbers) 2. Click **🎙️ Typecast TTS** → **Generate Selected Rows** 3. Only the selected rows will be processed Use "Generate Selected Rows" when you want to regenerate specific audio files or add new rows incrementally. ### Clear Audio URLs To remove all generated URLs (doesn't delete audio files from Drive): 1. Click **🎙️ Typecast TTS** → **Clear Audio URLs** 2. Confirm the action 3. All URLs in Column D will be cleared --- ## Advanced Configuration ### Smart Emotion (Default) The script uses **Smart Emotion** by default, which automatically analyzes your text and applies appropriate emotions: ```javascript prompt: { emotion_type: "smart"; // Automatically detects best emotion for your text } ``` ### Use Preset Emotions Instead If you want to manually control emotions, change `emotion_type` to `preset`: ```javascript function callTypecastAPI(text, voiceId, language) { const payload = { voice_id: voiceId, text: text, model: "ssfm-v30", language: language, prompt: { emotion_type: "preset", emotion_preset: "happy", // Options: happy, sad, angry, whisper, normal, toneup, or tonedown. emotion_intensity: 1.0, // 0.0 to 1.0 }, output: { audio_format: "mp3", audio_tempo: 1.0, // Speed: 0.5 (slow) to 2.0 (fast) audio_pitch: 0, // Pitch: -12 to +12 semitones volume: 100, // Volume: 0 to 200 }, }; // ... rest of the code } ``` ### Supported Languages The script supports **37 languages** with the ssfm-v30 model (used by default): | Code | Language | Code | Language | Code | Language | | ----- | --------- | ----- | ---------- | ----- | ---------- | | `ara` | Arabic | `ind` | Indonesian | `por` | Portuguese | | `ben` | Bengali | `ita` | Italian | `ron` | Romanian | | `bul` | Bulgarian | `jpn` | Japanese | `rus` | Russian | | `ces` | Czech | `kor` | Korean | `slk` | Slovak | | `dan` | Danish | `msa` | Malay | `spa` | Spanish | | `deu` | German | `nan` | Min Nan | `swe` | Swedish | | `ell` | Greek | `nld` | Dutch | `tam` | Tamil | | `eng` | English | `nor` | Norwegian | `tgl` | Tagalog | | `fin` | Finnish | `pan` | Punjabi | `tha` | Thai | | `fra` | French | `pol` | Polish | `tur` | Turkish | | `hin` | Hindi | `ukr` | Ukrainian | `vie` | Vietnamese | | `hrv` | Croatian | `yue` | Cantonese | `zho` | Chinese | | `hun` | Hungarian | | | | | Simply change the value in Column C to use different languages! Language codes are case-insensitive (`ENG` and `eng` both work). ### Save to Specific Drive Folder To save audio files to a specific folder instead of root: ```javascript // Replace this line: const folder = DriveApp.getRootFolder(); // With this (using folder ID): const folder = DriveApp.getFolderById("YOUR_FOLDER_ID_HERE"); // Or create a new folder: const folder = DriveApp.createFolder("Typecast Audio Files"); ``` ### Add More Columns You can extend the spreadsheet with additional columns for even more control: - **Column E**: Emotion Preset (happy, sad, angry, normal) - **Column F**: Audio Tempo (0.5 to 2.0) - **Column G**: Audio Pitch (-12 to +12) - **Column H**: Status (Processing, Done, Error) Then modify the script to read these values from the sheet. --- ## Why Use Google Sheets with Typecast? Simple copy-paste setup. Non-developers can easily generate professional voiceovers in just 5 minutes. Set it up once and use forever. Ideal for repetitive TTS tasks and macro-style workflows. Generate hundreds of audio files with a single click. Process entire content calendars at once. Share spreadsheets with your team. Everyone can contribute text and generate audio together. **This is how simple the Typecast API is!** With just a few lines of code and Google Sheets, you can automate your entire TTS workflow. Perfect for content creators, marketers, and educators who need to generate audio at scale without technical expertise. --- ## Use Cases Create course narrations from lesson scripts. Add text in Column A, generate audio, and download for your videos. Generate intro/outro segments, ad reads, and announcements from a shared Google Sheet. Convert e-commerce product descriptions into audio for accessibility or marketing videos. Batch-generate voiceovers for Instagram Reels, TikTok, or YouTube Shorts from your content calendar. Translate text in your sheet and generate audio in multiple languages for global audiences. --- ## Troubleshooting - Make sure you saved the script in Apps Script editor - Try running the `onOpen` function manually: 1. Go back to Apps Script editor 2. Select `onOpen` from the function dropdown 3. Click the **Run** button (▶️) - Check the browser console for errors (F12) - Make sure you clicked **Allow** when prompted - Try clearing authorization and re-authorizing: 1. Apps Script editor → Run → Clear authorization 2. Save and close 3. Refresh your spreadsheet 4. Try the menu again - Check that you replaced `YOUR_API_KEY_HERE` with your actual API key - Verify your API key at [Typecast API console](https://studio.typecast.ai/developers/api/) - Make sure there are no extra spaces around the key - Check your credit balance in the [Typecast API console](https://studio.typecast.ai/developers/api/usage) - Each character costs credits - make sure you have enough - Process in smaller batches using "Generate Selected Rows" - Apps Script has a 6-minute execution limit - For very large datasets (500+ rows), split into multiple sheets - Check your Google Drive root folder - Audio files are named `typecast_[timestamp].mp3` - Make sure the script has Drive permissions (authorized correctly) --- ## Resources Browse 600+ available voices Explore the Typecast API Learn more about Google Apps Script Get your Typecast API key --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Zapier [Zapier](https://zapier.com/) is the most popular workflow automation platform. With the Typecast integration, you can convert text to speech automatically - no coding required! ## What You Can Do With the Typecast Zapier integration, you can: - **Generate voiceovers** from any text automatically - **Choose from 600\+ voices** with different genders, ages, and styles - **Apply emotions** (happy, sad, angry, whisper, and more) - **Use Smart Emotion** for context-aware voice synthesis (ssfm-v30) - **Recommend voices** from a text description, then fetch voice details before synthesis when needed - **Connect to other apps** - send audio via email, save to cloud storage, post to Slack, etc. --- ## Prerequisites Before you start, make sure you have: 1. **Zapier account** - [Sign up here](https://zapier.com/sign-up) if you don't have one 2. **Typecast API Key** - [Get yours here](https://studio.typecast.ai/developers/api/) --- ## Installation ### Step 1: Connect Your Typecast Account When you first use Typecast in a Zap: 1. You'll be prompted to connect your Typecast account 2. Enter your **Typecast API Key** 3. Click **Yes, Continue** You can get your API key from the [Typecast API Console](https://studio.typecast.ai/developers/api/). --- ## Quick Start: Your First Voice Generation Let's create a simple Zap that generates speech every hour! ### Step 1: Create a New Zap 1. Go to [Zapier Dashboard](https://zapier.com/app/assets/zaps) 2. Click **\+ Create** → **New Zap** ![Zapier Zaps dashboard showing the Create button](/images/zapier-zaps-dashboard.webp) ### Step 2: Set Up the Trigger For this example, we'll use a Schedule trigger: 1. Click on the **Trigger** step 2. Search for **Schedule** and select it 3. Choose **Every Hour** as the event 4. Click **Continue** and **Test trigger** ![Image](/images/image-7.webp) ### Step 3: Add Typecast Action 1. Click on the **Action** step 2. Search for **Typecast** 3. Select **Typecast** ![Image](/images/image-8.webp) 4. Choose **Create Speech From Text** as the event ![Image](/images/image-9.webp) ### Step 4: Configure Text to Speech ![Image](/images/image-10.webp) | Setting | What to Enter | | --- | --- | | **Text** | Your text to convert (required) | | **Model** | `ssfm-v30 (Recommended)` - latest model with best quality | | **Voice** | Select from the dropdown (required) | | **Language** | Auto-detected, or select manually | | **Emotion Type** | `Preset` or `Smart` (v30 only) | | **Emotion Preset** | Normal, Happy, Sad, Angry, Whisper, etc. | ### Step 5: Test and Publish 1. Click **Continue** to go to the Test step 2. Click **Test step** to generate sample audio 3. If successful, click **Publish** to activate your Zap The generated audio URL will be available as output data. You can use it in subsequent steps to send via email, upload to cloud storage, or post to Slack. --- ## Available Actions ### Create Speech From Text Converts text to speech using Typecast AI voice models. **Inputs:** | Field | Required | Description | | --- | --- | --- | | Text | Yes | Text to convert (max 2000 characters) | | Model | Yes | `ssfm-v30` (recommended) or `ssfm-v21` | | Voice | Yes | Select from 600\+ available voices | | Language | No | ISO 639-3 code (auto-detected if not set) | | Emotion Type | No | `Preset` or `Smart` (context-aware, v30 only) | | Emotion Preset | No | Normal, Happy, Sad, Angry, Whisper, Tone Up, Tone Down | | Emotion Intensity | No | 0.0 to 2.0 (default: 1.0) | | Volume | No | 0 to 200 (default: 100) | | Audio Pitch | No | -12 to \+12 semitones (default: 0) | | Audio Tempo | No | 0.5x to 2.0x speed (default: 1.0) | | Audio Format | No | WAV or MP3 | | Seed | No | For reproducible results | **Outputs:** - Audio File URL - Speech ID - Duration (seconds) - Content Type ### List Voices (Search) Lists all available voice models with enhanced metadata. **Filters:** - Model (ssfm-v30, ssfm-v21) - Gender (Male, Female) - Age (Child, Teenager, Young Adult, Middle Age, Elder) - Use Cases (Audiobook, Podcast, E-learning, etc.) ### Get Voice by ID (Search) Get detailed information for a specific voice including supported emotions per model. ### Recommend Voices (Search) Find voice candidates from a text description. **Inputs:** | Field | Required | Description | | --- | --- | --- | | Query | Yes | Text describing the desired style, mood, language, use case, or content context | | Count | No | Number of recommendations to return (1-10, default: 5) | **Outputs:** - Voice ID - Voice Name - Score The recommendation response contains only `voice_id`, `voice_name`, and `score`; use List Voices or Get Voice by ID when a Zap needs detailed metadata before synthesis. --- ## Emotion Settings Make your voice expressive with emotion controls! ### For SSFM-V30 (Latest Model) Two ways to add emotion: AI automatically detects the best emotion from your text context. Perfect for natural conversations and storytelling. Add "Previous Text" and "Next Text" for better context understanding. Manually choose from 7 emotions: Normal, Happy, Sad, Angry, Whisper, Tone Up, Tone Down. ### For SSFM-V21 Choose from 4 emotions: `Normal`, `Happy`, `Sad`, `Angry` Adjust **Emotion Intensity** (0.0 - 2.0): - `0.0` - Completely neutral - `1.0` - Standard (default) - `2.0` - Maximum intensity --- ## Example Use Cases 1. **Trigger**: RSS feed with new articles 2. **Action**: Typecast creates audio from article summary 3. **Action**: Upload to podcast hosting platform 1. **Trigger**: New support ticket 2. **Action**: AI generates response text 3. **Action**: Typecast converts to voice message 4. **Action**: Send via email or SMS 1. **Trigger**: New lesson content in Google Sheets 2. **Action**: Typecast generates narration 3. **Action**: Upload to Google Drive 4. **Action**: Notify team via Slack --- ## Troubleshooting Search for "Typecast" in the Zapier app directory. Make sure you're using the latest available version. - Check your API key is correct - Verify your key at [Typecast API Console](https://studio.typecast.ai/developers/api/) - Make sure there are no extra spaces in the key - Check your API key has proper permissions - Try refreshing the field by clicking the refresh icon - Check that your text is not empty - Verify you have sufficient API credits - Check the error message in the test output --- ## Resources Browse all available voices Explore the Typecast API Zapier documentation and support ## Control silence duration In Typecast **2.2.7 or later**, set **Remaining Silence (ms)** in standard, streaming, or timestamp speech actions. The input key is `remove_silence_ms`; leave it blank to disable processing. `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Make [Make](https://www.make.com/) is a powerful visual workflow automation platform. With the Typecast integration, you can convert text to speech automatically - no coding required! ## What You Can Do With the Typecast Make integration, you can: - **Generate voiceovers** from any text automatically - **Choose from 600+ voices** with different genders, ages, and styles - **Apply emotions** (happy, sad, angry, whisper, and more) - **Use Smart Emotion** for context-aware voice synthesis (ssfm-v30) - **Filter voices** by gender, age group, and use case - **Connect to 1500+ apps** - send audio via email, save to cloud storage, post to Slack, etc. --- ## Prerequisites Before you start, make sure you have: 1. **Make account** - [Sign up here](https://www.make.com/en/register) if you don't have one 2. **Typecast API Key** - [Get yours here](https://studio.typecast.ai/developers/api/) 3. **Invite link access** - [Accept the invite](https://www.make.com/en/hq/app-invitation/19c62bf73b6afd22b41a2318e3f0a57e) --- ## Installation ### Step 1: Accept the Invite Since Typecast is currently in private beta, you need to accept the invite first: 1. Click the **[invite link](https://www.make.com/en/hq/app-invitation/19c62bf73b6afd22b41a2318e3f0a57e)** 2. Log in to your Make account (or create one) 3. Click **Install** to add Typecast to your apps ### Step 2: Connect Your Typecast Account When you first use Typecast in a scenario: 1. Click **Create a connection** 2. Enter a name for your connection (e.g., "My Typecast") 3. Enter your **Typecast API Key** 4. Click **Save** You can get your API key from the [Typecast API Console](https://studio.typecast.ai/developers/api/). --- ## Quick Start: Your First Voice Generation Let's create a simple scenario that generates speech! ### Step 1: Create a New Scenario 1. Go to [Make Dashboard](https://www.make.com/) 2. Click **+ Create a new scenario** ### Step 2: Add Typecast Module 1. Click the **+** button to add a module 2. Search for **Typecast** 3. Select **Typecast** from the results Searching for Typecast in Make ### Step 3: Select Action Choose **Generate a Speech** from the available actions. Typecast action options - Generate a Speech, Get Voices ### Step 4: Configure Text to Speech Typecast configuration fields in Make | Setting | What to Enter | |---------|---------------| | **Text** | Your text to convert (required) | | **Voice ID** | Enter a voice ID (e.g., `tc_60e5426de8b95f1d3000d7b5`) | | **Model** | `ssfm-v30` - latest model with best quality | | **Language** | Auto-detected, or select manually | | **Emotion Type** | `Preset` or `Smart` (ssfm-v30 only) | Use the **Get Voices** module first to find available Voice IDs. You can filter by gender, age, and use case! ### Step 5: Test and Activate 1. Click **OK** to save the module configuration 2. Click **Run once** to test your scenario 3. If successful, toggle the scenario to **ON** to activate it The generated audio will be returned as binary data (WAV or MP3). You can use it in subsequent modules to send via email, upload to cloud storage, or process further. --- ## Available Modules ### Generate a Speech (Action) Converts text to speech using Typecast AI voice models. **Inputs:** | Field | Required | Description | |-------|----------|-------------| | Text | Yes | Text to convert (max 2000 characters) | | Voice ID | Yes | Voice identifier (format: `tc_xxxxx`) | | Model | Yes | `ssfm-v30` (recommended) or `ssfm-v21` | | Language | No | ISO 639-3 code (auto-detected if not set) | | Emotion Type | No | `Preset` or `Smart` (context-aware, ssfm-v30 only) | | Emotion Preset | No | Normal, Happy, Sad, Angry, Whisper, Tone Up, Tone Down | | Emotion Intensity | No | 0.0 to 2.0 (default: 1.0) | | Target LUFS | No | -70 to 0 (e.g. -14 for streaming, -23 for broadcast) | | Pitch | No | -12 to +12 semitones (default: 0) | | Tempo | No | 0.5x to 2.0x speed (default: 1.0) | | Audio Format | No | WAV or MP3 | | Seed | No | For reproducible results | **Outputs:** - Audio file (binary data) - File name ### Get Voices (Search) Lists all available voice models with filtering options. **Filters:** | Field | Description | |-------|-------------| | Model | Filter by `ssfm-v30` or `ssfm-v21` | | Gender | Male or Female | | Age | Child, Teenager, Young Adult, Middle Age, Elder | | Use Cases | Audiobook, Podcast, E-learning, Ads, Game, etc. | | Limit | Maximum number of results | **Output (per voice):** - Voice ID - Voice Name - Supported Models and Emotions - Gender and Age Group - Recommended Use Cases --- ## Finding the Right Voice To find the perfect voice for your project: Add the **Get Voices** module to your scenario Filter by gender, age group, and use case to narrow down options From the results, copy the `voice_id` you want to use Paste the Voice ID into the **Generate a Speech** module --- ## Emotion Settings Make your voice expressive with emotion controls! ### For ssfm-v30 (Latest Model) Two ways to add emotion: AI automatically detects the best emotion from your text context. Perfect for natural conversations and storytelling. Add "Previous Text" and "Next Text" for better context understanding. Manually choose from 7 emotions: Normal, Happy, Sad, Angry, Whisper, Tone Up, Tone Down. When you select an **Emotion Type**, additional fields appear dynamically: - **Preset**: Shows "Emotion Preset" dropdown and "Emotion Intensity" slider - **Smart**: Shows "Previous Text" and "Next Text" context fields Preset emotion settings in Make Smart emotion settings with context in Make ### For ssfm-v21 Choose from 4 emotions: `Normal`, `Happy`, `Sad`, `Angry` Adjust **Emotion Intensity** (0.0 - 2.0): - `0.0` - Completely neutral - `1.0` - Standard (default) - `2.0` - Maximum intensity --- ## Model Comparison | Feature | ssfm-v21 | ssfm-v30 | |---------|----------|----------| | Emotions | 4 types | 7 types | | Languages | 27 | 37 | | Smart Mode | No | Yes | | Quality | Stable | Enhanced | | Recommendation | Production | Latest Features | --- ## Example Use Cases 1. **Trigger**: New row added in Google Sheets 2. **Action**: Typecast generates voiceover from script text 3. **Action**: Upload audio to Google Drive 4. **Action**: Notify team via Slack 1. **Trigger**: New content in CMS 2. **Action**: Translate text with DeepL or Google Translate 3. **Action**: Typecast creates audio in each language 4. **Action**: Save to cloud storage organized by language 1. **Trigger**: New lesson module approved 2. **Action**: Typecast generates professional narration 3. **Action**: Upload to LMS (Teachable, Thinkific, etc.) 4. **Action**: Update course status in Airtable 1. **Trigger**: Scheduled daily at 6 AM 2. **Action**: Fetch latest news headlines 3. **Action**: Typecast creates intro with energetic voice 4. **Action**: Append to podcast audio file --- ## Advanced: Working with Audio Output The **Generate a Speech** module outputs audio as binary data. Here's how to use it: ### Save to Cloud Storage Connect a **Google Drive**, **Dropbox**, or **OneDrive** module after Typecast: - Map the audio data to the file content - Set filename with `.wav` or `.mp3` extension based on your Audio Format setting ### Send via Email Use **Gmail** or **SMTP** module: - Add the audio as an attachment - Map the file data and set the correct MIME type (`audio/wav` or `audio/mpeg`) ### Process Further Use **HTTP** module to: - Upload to a custom server - Send to a video editing API - Store in your own database --- ## Troubleshooting Make sure you've accepted the [invite link](https://www.make.com/en/hq/app-invitation/19c62bf73b6afd22b41a2318e3f0a57e) first. Typecast is currently in private beta. - Check your API key is correct - Verify your key at [Typecast API Console](https://studio.typecast.ai/developers/api/) - Make sure there are no extra spaces in the key - Voice IDs must start with `tc_` prefix - Use the **Get Voices** module to find valid Voice IDs - Make sure the voice supports your selected model - Check that your text is not empty - Verify you have sufficient API credits - Check if the text exceeds 2000 characters - Smart Emotion is only available with `ssfm-v30` model - Make sure you've selected "Smart" as the Emotion Type - Provide context in Previous Text and/or Next Text fields - Try switching between WAV and MP3 formats - Adjust Target LUFS, Pitch, and Tempo settings - Use a different voice that better suits your content --- ## Supported Languages Typecast supports 37 languages with the ssfm-v30 model: English, Korean, Japanese, Chinese, Spanish, French, German, Portuguese Bulgarian, Croatian, Czech, Danish, Dutch, Finnish, Greek, Hungarian, Italian, Norwegian, Polish, Romanian, Russian, Slovak, Swedish, Turkish, Ukrainian Arabic, Bengali, Cantonese, Hindi, Indonesian, Malay, Min Nan, Punjabi, Tagalog, Tamil, Thai, Vietnamese --- ## Resources Accept the invite to use Typecast Browse all available voices Explore the Typecast API Make documentation and support --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # n8n [n8n](https://n8n.io/) is a visual workflow automation tool. With the Typecast node, you can convert text to speech automatically - no coding required! ## What You Can Do With the Typecast n8n node, you can: - **Automate content creation** - Build fully automated YouTube or TikTok channels by combining RSS feeds, AI writing, and Typecast voices. - **Enhance customer experience** - Send personalized voice messages via WhatsApp or email when a customer makes a purchase. - **Scale your reach** - Automatically translate and dub your podcast or video content into multiple languages. - **Recommend voices** - Search for voice candidates from a natural-language description. - **Stay notified** - Get custom voice alerts on Slack or Discord for critical system updates or sales milestones. --- ## Prerequisites Before you start, make sure you have: 1. **n8n** installed ([n8n Cloud](https://n8n.io/) or self-hosted) 2. **Typecast API Key** - [Get yours here](https://studio.typecast.ai/developers/api/) --- ## Installation ### Step 1: Install the Typecast Node If you are using n8n Cloud, no separate installation is required. You can skip directly to the **Quick Start** section. If an installation button appears when searching for the node, click **Install node** to complete the setup. 1. Run the following command in your n8n installation directory: ```bash npm install @neosapience/n8n-nodes-typecast ``` 2. Restart n8n. --- ## Quick Start: Your First Voice Generation Let's create your first text-to-speech workflow! ### Step 1: Add the Typecast Node 1. Create a new workflow 2. Click **+** to add a node 3. Search for **Typecast** 4. Select **Typecast** Searching for Typecast node in n8n 5. Choose an action from the list (e.g., **Convert text to speech**) Typecast node actions list ### Step 2: Connect Your API Key (Credential) After selecting the node, you need to configure your API key to connect with the Typecast API. 1. In the node settings panel, click on the **Credential to connect with** field. 2. Select **- Create New Credential -**. 3. Enter your API key, which you can copy from the [Typecast API console](https://studio.typecast.ai/developers/api/). 4. Click **Create** to save your credentials. Creating new credential in n8n ### Step 3: Configure Text to Speech | Setting | What to Enter | |---------|---------------| | **Resource** | `Speech` | | **Operation** | `Text to Speech` | | **Voice ID** | Select a voice from the dropdown (shows name, gender, age, and emotions) | | **Text** | Your text to convert | | **Model** | `ssfm-v30` - recommended for best quality | #### Selecting a Voice The Voice ID field makes it easy to find the perfect voice: 1. Click on the Voice ID dropdown 2. Browse voices with their details (name, gender, age, available emotions) 3. Use the search to filter by name or characteristics 4. Select your preferred voice Voice ID dropdown with voice details You can also switch to "By ID" mode to enter a Voice ID directly (e.g., `tc_60e5426de8b95f1d3000d7b5`). Typecast Text to Speech configured #### Emotion Settings Make your voice expressive with emotion controls! **For ssfm-v30** Two ways to add emotion: AI automatically detects the best emotion from your text context. Perfect for natural conversations and storytelling. Manually choose from 7 emotions: Normal, Happy, Sad, Angry, Whisper, Tone Up, Tone Down. **Smart Emotion Tip:** Add "Previous Text" and "Next Text" to help the AI understand context better! **For ssfm-v21** Choose from 4 emotions: `Normal`, `Happy`, `Sad`, `Angry` Emotion preset dropdown showing Normal, Happy, Sad, Angry options Adjust **Emotion Intensity** (0.0 - 2.0): - `0.0` - Completely neutral - `1.0` - Standard (default) - `2.0` - Maximum intensity Emotion settings for ssfm-v21 model #### Additional Options Customize your audio output: | Option | Description | Default | |--------|-------------|---------| | **Audio Format** | `WAV` (high quality) or `MP3` (smaller size) | WAV | | **Audio Pitch** | Adjust pitch (-12 to +12 semitones) | 0 | | **Audio Tempo** | Speed adjustment (0.5x to 2.0x) | 1.0 | | **Language** | Override auto-detection if needed | Auto-detect | | **Seed** | Unsigned integer (≥ 0). Use same seed for reproducible output | Random | ### Step 4: Run and Listen 1. Connect the nodes (Manual Trigger → Typecast) 2. Click **Execute Workflow** 3. Check the output - your audio file is ready! 4. Click the audio to play it n8n workflow with connected Typecast node The generated audio appears as a binary file named `data`. You can save it, email it, or send it anywhere! --- ## Finding the Perfect Voice ### Browse All Voices 1. Add a Typecast node 2. Set **Resource** → `Voice` 3. Set **Operation** → `Get All Voices` 4. Run the node to see all available voices ### Recommend Voices 1. Add a Typecast node 2. Set **Resource** → `Voice` 3. Set **Operation** → `Recommend Voices` 4. Enter a text description and run the node Recommendation results contain only `voice_id`, `voice_name`, and `score`. Use **Get Voice** or **Get All Voices** when your workflow needs metadata such as supported models, emotions, gender, age, or use cases before synthesis. ### Filter Voices Use filters to find exactly what you need: | Filter | Options | |--------|---------| | **Model** | `ssfm-v30` or `ssfm-v21` | | **Gender** | `Male` or `Female` | | **Age** | `Child`, `Teenager`, `Young Adult`, `Middle Age`, `Elder` | | **Use Cases** | `Audiobook`, `Ads`, `E-learning`, `Game`, `Podcast`, and more | Voice filter options in Typecast node --- ## Troubleshooting - Restart n8n completely - Clear your browser cache - Verify the installation in **Settings** → **Community Nodes** - Check your API key is correct - Verify your key at [Typecast API Console](https://studio.typecast.ai/developers/api/) - Make sure there are no extra spaces in the key - Use the **Get All Voices** operation to find valid Voice IDs - Voice IDs are case-sensitive (use lowercase `tc_...`) - Check that your text is not empty - Verify you have sufficient API credits - Check the error message in the node output --- ## Resources View on npm registry View source code and contribute Browse all available voices Explore the Typecast API ## Control silence duration In speech actions with `@neosapience/n8n-nodes-typecast` **1.2.5 or later**, add **Additional Options → Remaining Silence (Ms)**. The displayed `300` is the initial value only after adding the option; existing workflows that omit it are unchanged. `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # MCP Connect this [Model Context Protocol](https://modelcontextprotocol.io/docs/getting-started/intro) to give your AI complete Typecast knowledge, integrate Typecast TTS into your service in minutes. ## One-Click Install For Cursor and Replit, just click the button below: One-click install for Cursor IDE One-click install for Replit ### Other MCP Clients For other MCP-compatible clients, add this URL: ``` https://typecast.ai/docs/mcp ``` ```bash claude mcp add --transport http typecast-helper https://typecast.ai/docs/mcp ``` 1. Open Windsurf Settings 2. Navigate to **Cascade** → **MCP Servers** 3. Click **"Add Server"** → **"Add Remote MCP Server"** 4. Enter URL: `https://typecast.ai/docs/mcp` Add to your `.vscode/mcp.json`: ```json { "servers": { "typecast-helper": { "url": "https://typecast.ai/docs/mcp" } } } ``` That's it! You can now ask your AI assistant to help you integrate Typecast TTS into your projects. ### What You Can Do Once connected, your AI assistant gains knowledge about: - **API Integration** - Get code examples for any language - **Voice Selection** - Find the perfect voice for your use case - **Best Practices** - Learn optimal settings for different scenarios - **Troubleshooting** - Quick solutions for common issues ```plaintext Example: Quick Integration "Integrate Typecast TTS into my project." ``` --- The setup above is all you need. The section below is optional for advanced use cases. --- ## Advanced: Automated TTS Generation Need to generate audio files automatically? The hosted or self-hosted Typecast API MCP server lets your AI assistant **directly call the Typecast API** to create audio on demand, perfect for batch processing and automated workflows. Both options also provide `recommend_voices` for natural-language voice search. Recommendation results contain only `voice_id`, `voice_name`, and `score`, so call `get_voice` or `get_voices` when your workflow needs metadata such as supported models, emotions, gender, age, or use cases. ### What Makes This Different | Docs MCP | Hosted API MCP | Self-hosted API MCP | |----------|----------------|---------------------| | Provides knowledge and guidance | Calls Typecast API directly | Calls Typecast API directly | | No API key required | No local installation | Runs on your computer | | Best for integration help | Best for quick automation | Best for local files and audio playback | ### Prerequisites - Typecast API key ([Get yours here](https://studio.typecast.ai/developers/api/)) - [uv](https://docs.astral.sh/uv/) package manager (self-hosted only) ### Setup Add the hosted Streamable HTTP endpoint to an MCP client that supports custom headers: ```json { "mcpServers": { "typecast": { "url": "https://typecast.ai/docs/mcp", "headers": { "X-API-KEY": "YOUR_API_KEY" } } } } ``` Without the API key, the hosted server exposes only `search_documentation`. Authenticated requests unlock the Typecast API tools. Generated audio is returned as a private download URL that expires after one hour. `play_audio` is available only when self-hosting. For hosted voice cloning, send `audio_base64` and an `.mp3` or `.wav` `audio_filename`. Edit `~/Library/Application Support/Claude/claude_desktop_config.json`: ```json { "mcpServers": { "typecast": { "command": "uvx", "args": [ "--from", "git+https://github.com/neosapience/typecast-api-mcp-server.git", "typecast-api-mcp-server" ], "env": { "TYPECAST_API_KEY": "YOUR_API_KEY", "TYPECAST_OUTPUT_DIR": "/Users/yourname/Downloads/typecast_output" } } } } ``` Edit `%APPDATA%\Claude\claude_desktop_config.json`: ```json { "mcpServers": { "typecast": { "command": "uvx", "args": [ "--from", "git+https://github.com/neosapience/typecast-api-mcp-server.git", "typecast-api-mcp-server" ], "env": { "TYPECAST_API_KEY": "YOUR_API_KEY", "TYPECAST_OUTPUT_DIR": "C:\\Users\\yourname\\Downloads\\typecast_output" } } } } ``` Edit `~/.config/Claude/claude_desktop_config.json`: ```json { "mcpServers": { "typecast": { "command": "uvx", "args": [ "--from", "git+https://github.com/neosapience/typecast-api-mcp-server.git", "typecast-api-mcp-server" ], "env": { "TYPECAST_API_KEY": "YOUR_API_KEY", "TYPECAST_OUTPUT_DIR": "/home/yourname/Downloads/typecast_output", "XDG_RUNTIME_DIR": "/run/user/1000" } } } } ``` Linux requires `XDG_RUNTIME_DIR` for audio playback. Add to your MCP settings in Cursor: ```json { "mcpServers": { "typecast": { "command": "uvx", "args": [ "--from", "git+https://github.com/neosapience/typecast-api-mcp-server.git", "typecast-api-mcp-server" ], "env": { "TYPECAST_API_KEY": "YOUR_API_KEY", "TYPECAST_OUTPUT_DIR": "/path/to/output" } } } } ``` ```bash # Add the Typecast MCP server claude mcp add --transport stdio \ --env TYPECAST_API_KEY=YOUR_API_KEY \ --env TYPECAST_OUTPUT_DIR=/path/to/output \ typecast -- uvx --from git+https://github.com/neosapience/typecast-api-mcp-server.git typecast-api-mcp-server ``` ### Troubleshooting - Verify the configuration is correct - Restart your application completely - Check that `uv` is installed and available in your PATH - Confirm your API key is set correctly in the config - Verify your key at [Typecast API](https://studio.typecast.ai/developers/api/) - Set `XDG_RUNTIME_DIR` environment variable - Check audio device: `aplay -l` --- ## Resources View source code for Self-hosted MCP Server Explore the Typecast API Browse available voices Build custom integrations ## Control silence duration The hosted **Typecast API MCP server** supports optional `remove_silence_ms` in standard, streaming, and timestamp TTS tools. Pass `"remove_silence_ms": 300` as a tool argument. This API execution server is distinct from the documentation-search `/docs/mcp`; update self-hosted instances to a revision that includes this option. `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. Standard, streaming, and timestamp TTS use `output.remove_silence_ms`; Compose uses `segments[].output.remove_silence_ms` on each `tts` segment. Returned timestamps align with the processed audio, and explicit `pause` segments are preserved. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Pipecat [Pipecat](https://github.com/pipecat-ai/pipecat) is an open-source framework for building real-time, multimodal AI voice agents. With the Typecast TTS integration, you can add high-quality neural voices with emotion control to your voice AI pipelines. ## What is Pipecat? Pipecat is a Python framework that simplifies building voice AI applications. It connects various services (speech-to-text, LLMs, text-to-speech) into a unified pipeline, handling the complexity of real-time audio streaming, turn-taking, and transport protocols. A typical Pipecat pipeline looks like this: ``` User Audio → STT → LLM → TTS → Bot Audio ``` The Typecast TTS service (`pipecat-ai-typecast`) integrates seamlessly into this pipeline, converting LLM responses into expressive speech. --- ## What You Can Do With the Typecast Pipecat integration, you can: - **Build voice AI agents** with natural, expressive voices - **Choose from 600+ voices** with different genders, ages, and styles - **Apply emotions** (happy, sad, angry, whisper, and more) - **Use Smart Emotion** for context-aware voice synthesis - **Deploy anywhere** - Daily, Twilio, or native WebRTC --- ## Prerequisites Before you start, make sure you have: | Requirement | Version | |-------------|---------| | Python | 3.10+ | | Pipecat | v0.0.94+ | | Typecast API Key | [Get yours here](https://studio.typecast.ai/developers/api/) | --- ## Installation Install the Typecast TTS service for Pipecat: ```bash pip install pipecat-ai-typecast ``` Using uv? Run `uv add pipecat-ai-typecast` instead. --- ## Quick Start Here's a minimal example of integrating Typecast TTS into a Pipecat pipeline: ```python import os import aiohttp from pipecat.pipeline.pipeline import Pipeline from pipecat_typecast import TypecastTTSService async with aiohttp.ClientSession() as session: # Initialize Typecast TTS tts = TypecastTTSService( aiohttp_session=session, api_key=os.getenv("TYPECAST_API_KEY"), voice_id=os.getenv("TYPECAST_VOICE_ID", "tc_672c5f5ce59fac2a48faeaee"), ) # Build your pipeline pipeline = Pipeline([ transport.input(), # User audio input stt, # Speech-to-text context_aggregator.user(), # Add user text to context llm, # LLM generates response tts, # Typecast TTS synthesis transport.output(), # Stream audio to user context_aggregator.assistant(), # Store assistant response ]) ``` Set your environment variables: - `TYPECAST_API_KEY` - Your Typecast API key (required) - `TYPECAST_VOICE_ID` - Voice to use (optional, defaults to a preset voice) --- ## Configuration The `TypecastTTSService` supports both preset-based and context-aware emotion control. ### Basic Configuration ```python from pipecat_typecast import TypecastTTSService tts = TypecastTTSService( aiohttp_session=session, api_key=os.getenv("TYPECAST_API_KEY"), voice_id="tc_672c5f5ce59fac2a48faeaee", model="ssfm-v30", # Latest model (default) ) ``` ### Preset Emotion Control Choose from predefined emotions for consistent voice styling: ```python from pipecat_typecast import ( TypecastTTSService, TypecastInputParams, PresetPromptOptions, OutputOptions, ) params = TypecastInputParams( prompt_options=PresetPromptOptions( emotion_preset="happy", # normal | happy | sad | angry | whisper | toneup | tonedown emotion_intensity=1.3, # 0.0 - 2.0 ), output_options=OutputOptions( volume=110, # 0 - 200 (percent) audio_pitch=2, # -12 to 12 (semitones) audio_tempo=1.05, # 0.5 - 2.0 (playback speed) ), ) tts = TypecastTTSService( aiohttp_session=session, api_key=os.getenv("TYPECAST_API_KEY"), params=params, ) ``` ### Smart Emotion (Context-Aware) Let the AI automatically infer emotion from surrounding text: ```python from pipecat_typecast import ( TypecastTTSService, TypecastInputParams, SmartPromptOptions, ) params = TypecastInputParams( prompt_options=SmartPromptOptions( previous_text="I just got the best news ever!", # max 2000 chars next_text="I can't wait to share this with everyone!", ), ) tts = TypecastTTSService( aiohttp_session=session, api_key=os.getenv("TYPECAST_API_KEY"), params=params, ) ``` Manually choose from 7 emotions: Normal, Happy, Sad, Angry, Whisper, Tone Up, Tone Down. Best for consistent voice styling. AI automatically detects the best emotion from text context. Best for natural conversations. ### Parameter Reference | Parameter | Range | Description | |-----------|-------|-------------| | `emotion_preset` | varies by voice | ssfm-v30: `normal`, `happy`, `sad`, `angry`, `whisper`, `toneup`, `tonedown` | | `emotion_intensity` | 0.0 - 2.0 | Values > 1.0 increase expressiveness | | `audio_pitch` | -12 to 12 | Semitone adjustment | | `audio_tempo` | 0.5 - 2.0 | Recommended: 0.85 - 1.15 | | `volume` | 0 - 200 | Audio volume as percentage | | `seed` | uint32 | Unsigned integer seed for deterministic synthesis (≥ 0) | --- ## Supported Transports Pipecat supports multiple transport protocols. Typecast works with all of them: [Daily](https://www.daily.co/) provides WebRTC-based video and audio infrastructure. ```python from pipecat.transports.daily.transport import DailyParams transport_params = DailyParams( audio_in_enabled=True, audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ) ``` [Twilio](https://www.twilio.com/) enables voice calls over phone networks. ```python from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams transport_params = FastAPIWebsocketParams( audio_in_enabled=True, audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ) ``` Native WebRTC for browser-based applications. ```python from pipecat.transports.base_transport import TransportParams transport_params = TransportParams( audio_in_enabled=True, audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ) ``` --- ## Complete Example Here's a full working example that creates a voice AI agent: ```python import os import aiohttp from dotenv import load_dotenv from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.daily.transport import DailyParams, DailyTransport from pipecat_typecast import TypecastTTSService load_dotenv() async def main(): async with aiohttp.ClientSession() as session: # Initialize services stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY")) tts = TypecastTTSService( aiohttp_session=session, api_key=os.getenv("TYPECAST_API_KEY"), ) # Set up conversation context messages = [ { "role": "system", "content": "You are a helpful AI assistant. Keep responses concise.", }, ] context = LLMContext(messages) context_aggregator = LLMContextAggregatorPair(context) # Configure transport transport = DailyTransport( room_url=os.getenv("DAILY_ROOM_URL"), token=os.getenv("DAILY_TOKEN"), params=DailyParams( audio_in_enabled=True, audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), ) # Build and run pipeline pipeline = Pipeline([ transport.input(), stt, context_aggregator.user(), llm, tts, transport.output(), context_aggregator.assistant(), ]) task = PipelineTask(pipeline, params=PipelineParams()) runner = PipelineRunner() await runner.run(task) if __name__ == "__main__": import asyncio asyncio.run(main()) ``` --- ## Legacy Model (ssfm-v21) If you need to use the legacy ssfm-v21 model: ```python from pipecat_typecast import ( TypecastTTSService, TypecastInputParams, PromptOptions, ) params = TypecastInputParams( prompt_options=PromptOptions( emotion_preset="happy", # normal | happy | sad | angry emotion_intensity=1.3, ), ) tts = TypecastTTSService( aiohttp_session=session, api_key=os.getenv("TYPECAST_API_KEY"), model="ssfm-v21", params=params, ) ``` Note: ssfm-v21 supports fewer emotion presets (no `whisper`, `toneup`, `tonedown`). --- ## Troubleshooting - Ensure `TYPECAST_API_KEY` environment variable is set - Verify your key at [Typecast API Console](https://studio.typecast.ai/developers/api/) - Check for extra spaces in the key - Confirm your transport is configured with `audio_out_enabled=True` - Check that the TTS service is included in your pipeline - Verify your API key has sufficient credits - Adjust `audio_tempo` within the recommended range (0.85 - 1.15) - Try different `emotion_intensity` values - Ensure sample rate matches your transport configuration - Make sure you installed `pipecat-ai-typecast`, not just `pipecat-typecast` - Verify Python version is 3.10 or higher - Check that Pipecat version is v0.0.94 or later --- ## Resources Source code and examples Install via pip Learn more about Pipecat Browse available voices ## Control silence duration Supported for standard and streaming TTS in `pipecat-ai-typecast` **0.3.1 or later**. ```python from pipecat_typecast import TypecastInputParams, OutputOptions params = TypecastInputParams( output_options=OutputOptions(remove_silence_ms=300), ) ``` Pass this as `TypecastTTSService(..., params=params)`. `remove_silence_ms` specifies the **silence duration to retain**, not the amount to remove. Use an integer from `0` to `1000` ms. `0` removes detected silence; omission or `null` disables duration-based silence removal. `TypecastTTSService` forwards this setting as `output.remove_silence_ms` for standard and streaming TTS. This integration does not expose timestamp TTS or Compose. Streaming's default leading-silence trimming is separate. Small values such as `0` can leave gaps between playable chunks; allow sufficient playback buffering and test with your content. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # LlamaIndex [LlamaIndex](https://www.llamaindex.ai/) is a powerful data framework for building LLM applications. The Typecast tool integration allows your AI agents to generate expressive speech from text with emotion control. ## What is LlamaIndex? LlamaIndex is a Python framework for building context-augmented LLM applications. It provides tools for data ingestion, indexing, and querying, as well as agent capabilities that can use external tools. With the Typecast tool, your LlamaIndex agents can: - **Generate speech** from text with customizable voices - **Control emotions** (happy, sad, angry, whisper, and more) - **Discover voices** by filtering model, gender, age, or use case - **Create reproducible audio** using seed parameters --- ## Prerequisites Before you start, make sure you have: | Requirement | Version | |-------------|---------| | Python | 3.11+ | | LlamaIndex Core | 0.13–0.14 | | Typecast API Key | [Get yours here](https://studio.typecast.ai/developers/api/) | --- ## Installation Install the Typecast tool for LlamaIndex: ```bash pip install llama-index-tools-typecast ``` For agent usage, also install an LLM provider: `pip install llama-index-llms-openai` --- ## Quick Start Here's a minimal example of using Typecast TTS with a LlamaIndex agent: ```python from llama_index.tools.typecast import TypecastToolSpec from llama_index.core.agent.workflow import FunctionAgent from llama_index.llms.openai import OpenAI # Initialize the Typecast tool speech_tool = TypecastToolSpec(api_key="your-typecast-key") # Create an agent with Typecast capabilities agent = FunctionAgent( tools=speech_tool.to_tool_list(), llm=OpenAI(model="gpt-4o-mini"), ) # Generate speech through the agent result = await agent.run( 'Create speech from the text "Hello world!" with a happy emotion ' 'and output the file to "speech.wav"' ) print(result) ``` Set your environment variables: - `OPENAI_API_KEY` - Your OpenAI API key (for the agent's LLM) - Use your Typecast API key directly in the `TypecastToolSpec` constructor --- ## Available Tools The `TypecastToolSpec` provides three tools for your agents: Convert text to speech with emotion, pitch, tempo control, and reproducible results. List all available Typecast voices with optional filtering. Get details of a specific voice by ID. --- ## Direct Usage (Without Agent) You can also use the tool directly for more control: ### Discover Voices ```python from llama_index.tools.typecast import TypecastToolSpec speech_tool = TypecastToolSpec(api_key="your-typecast-key") # Get all available voices with optional filters voices = speech_tool.get_voices( model="ssfm-v30", gender="female", age="young_adult", use_case="Audiobook" ) print(f"Found {len(voices)} voices") for voice in voices: print(f"{voice['voice_name']} ({voice['voice_id']})") ``` ### Get Voice Details ```python # Get specific voice information voice = speech_tool.get_voice("tc_62a8975e695ad26f7fb514d1") print(f"Voice: {voice['voice_name']}") print(f"Gender: {voice.get('gender')}, Age: {voice.get('age')}") print(f"Use cases: {voice.get('use_cases')}") # Models include supported emotions for model in voice["models"]: print(f"Model {model['version']}: emotions = {model['emotions']}") ``` ### Generate Speech ```python # Text-to-speech with full parameter control output_path = speech_tool.text_to_speech( text="Hello world! This is a test.", voice_id="tc_62a8975e695ad26f7fb514d1", output_path="speech.wav", model="ssfm-v30", language="eng", emotion_preset="happy", emotion_intensity=1.5, volume=100, audio_pitch=0, audio_tempo=1.0, audio_format="wav", seed=42, # Unsigned seed for reproducible results ) print(f"Audio saved to: {output_path}") ``` --- ## Features ### Multiple Voice Models Typecast supports multiple AI voice model versions: | Model | Description | |-------|-------------| | `ssfm-v30` | Latest model with enhanced emotions (recommended) | | `ssfm-v21` | Legacy model for backward compatibility | ### Emotion Control Control the emotional expression of generated speech: | Emotion | ssfm-v30 | ssfm-v21 | |---------|----------|----------| | `normal` | ✓ | ✓ | | `happy` | ✓ | ✓ | | `sad` | ✓ | ✓ | | `angry` | ✓ | ✓ | | `whisper` | ✓ | - | | `toneup` | ✓ | - | | `tonedown` | ✓ | - | Use `emotion_intensity` (0.0 - 2.0) to adjust expressiveness. Values greater than 1.0 increase intensity. ### Multi-Language Support Typecast supports 27+ languages including: - English (`eng`) - Korean (`kor`) - Japanese (`jpn`) - Chinese (`zho`) - Spanish (`spa`) - And many more... ### Audio Customization Fine-tune your audio output: | Parameter | Range | Description | |-----------|-------|-------------| | `volume` | 0 - 200 | Audio volume as percentage | | `audio_pitch` | -12 to 12 | Semitone adjustment | | `audio_tempo` | 0.5 - 2.0 | Playback speed (recommended: 0.85 - 1.15) | | `audio_format` | `wav`, `mp3` | Output format | | `seed` | uint32 | Unsigned integer seed for reproducible audio generation (≥ 0) | --- ## Complete Agent Example Here's a full example with an agent that can discover voices and generate speech: ```python import os from llama_index.tools.typecast import TypecastToolSpec from llama_index.core.agent.workflow import FunctionAgent from llama_index.llms.openai import OpenAI # Set up API keys os.environ["OPENAI_API_KEY"] = "your-openai-key" # Initialize Typecast tool speech_tool = TypecastToolSpec(api_key="your-typecast-key") # Create agent with Typecast capabilities agent = FunctionAgent( tools=speech_tool.to_tool_list(), llm=OpenAI(model="gpt-4o-mini"), ) # Let the agent discover voices and generate speech result = await agent.run( 'Get the list of available voices, select the first female voice, ' 'and use it to create speech from the text "Welcome to Typecast!" ' 'with a happy emotion, saving to "welcome.wav"' ) print(result) ``` --- ## Troubleshooting - Ensure you're passing the correct API key to `TypecastToolSpec` - Verify your key at [Typecast API Console](https://studio.typecast.ai/developers/api/) - Check for extra spaces in the key - Check that the output path is writable - Verify your API key has sufficient credits - Ensure the voice_id is valid - Make sure you installed `llama-index-tools-typecast` - For agent usage, also install `llama-index-llms-openai` or your preferred LLM provider - Verify Python version is 3.11 or higher - Verify `llama-index-core` is version 0.13 or 0.14 - Be specific in your prompts about what you want the agent to do - Break down complex tasks into simpler steps - Provide example output paths for audio files --- ## Resources Source code and examples View on LlamaHub Learn more about LlamaIndex Browse available voices --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Postman Use our public Postman collection to quickly test Typecast API endpoints without writing any code. ## Get Started Visit our [Typecast Developers Postman Collection](https://www.postman.com/typecast-api-team/typecast-developers/overview). Click **"Fork"** to add the collection to your Postman workspace. In your forked collection, set the `X-API-KEY` header with your API key: ``` X-API-KEY: YOUR_API_KEY ``` Send requests to any endpoint and see responses in real-time. --- ## Resources Explore all Typecast API endpoints Create your Typecast API key --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 개요 타입캐스트는 AI 기술을 활용하여 자연스러운 음성 내레이션을 생성할 수 있는 텍스트 음성 변환(TTS) 플랫폼입니다. 다양한 합성 음성을 제공하며, 여러 언어로 텍스트를 음성으로 변환할 수 있습니다. **AI 에이전트로 개발하시나요?** 아래 프롬프트를 복사해 에이전트에게 전달하세요. ```text https://typecast.ai/docs/llms.txt를 먼저 읽고, Typecast API로 구현하는 방법을 도와주세요. ``` 타입캐스트가 처음이라면 타입캐스트 문서 상단의 **어시스턴트에게 묻기** 버튼을 사용하세요. "Next.js 앱에는 어떤 SDK를 쓰면 돼?", "오디오를 스트리밍하려면?", "자막 생성이 가장 빠른 방법을 보여줘"처럼 물어볼 수 있습니다. ## 타입캐스트 API가 제공하는 것 ssfm-v30 모델 기준 한국어, 영어, 일본어, 중국어, 스페인어, 베트남어 등 37개 언어로 음성을 생성할 수 있습니다. 모델, 성별, 연령대, 사용 사례별로 보이스를 고르거나, 퀵 클로닝으로 커스텀 보이스를 만들 수 있습니다. 감정, 속도, 포맷, 언어를 제어하면서 API와 SDK 전반에서 일관된 보이스 메타데이터를 사용할 수 있습니다. ## 핵심 API 기능 타입캐스트 API는 완성형 오디오 생성, 실시간 재생, 자막 타이밍, 커스텀 보이스 생성을 지원합니다. 텍스트를 WAV 또는 MP3 오디오 파일로 변환해 앱, 영상, 내레이션, 교육 콘텐츠, 음성 제품에 사용할 수 있습니다. 전체 합성 결과를 기다리지 않고 도착한 오디오 청크부터 재생합니다. 음성 에이전트, 인터랙티브 앱, 저지연 재생에 유용합니다. 단어 또는 문자 단위 정렬 데이터를 함께 받아 자막, 가라오케 하이라이트, 립싱크를 구현합니다. 짧은 오디오 샘플로 커스텀 보이스를 만들고, 기본 보이스와 동일하게 TTS 요청에 사용할 수 있습니다. ## 연동 방법 선택하기 타입캐스트가 무엇을 제공하는지 확인했다면, 구현 방식에 맞는 경로에서 시작하세요. Claude, Cursor, OpenClaw 같은 에이전트가 타입캐스트 문서를 읽고 연동 코드를 만들게 하세요. 직접 HTTP 호출을 새로 만들게 하기보다 사용하는 언어의 공식 SDK를 먼저 쓰도록 지시하는 것이 좋습니다. 퀵스타트를 따라 API 키를 만들고, 보이스를 고르고, 첫 TTS 요청을 실행하세요. 정확한 요청·응답 필드가 필요할 때는 API 레퍼런스를 보면 됩니다. ## 직접 구현할 때 API 키를 만들고 첫 오디오 파일을 생성합니다. Python, JavaScript, Go, Rust, C#, Java, Kotlin, C, Swift, Zig, PHP, Dart, Ruby SDK를 사용할 수 있습니다. 엔드포인트, 요청 파라미터, 응답 스키마, Try It 예시를 확인합니다. ## AI 에이전트로 개발할 때 AI 에이전트에게 타입캐스트 연동을 맡긴다면 아래 문서 중 하나를 먼저 제공하세요. Claude Code와 Claude Desktop에 적합합니다. 에이전트가 타입캐스트 작업 지시와 예시를 함께 참고할 수 있습니다. 원격 MCP 문서나 셀프호스트 타입캐스트 MCP 서버를 연결할 수 있는 에이전트에 적합합니다. 셸 명령을 실행하거나 cast CLI, MCP 도구를 연결하는 로컬 에이전트 워크플로우에 적합합니다. 에이전트에게는 먼저 사용하는 언어의 공식 SDK 문서를 참고하라고 지시하세요. SDK에는 TTS, 스트리밍, 타임스탬프 TTS, 자막 내보내기, 보이스 조회, 오류 처리 헬퍼가 포함되어 있습니다. ## 노코드 툴로 사용할 때 전체 앱을 직접 개발하지 않고 음성 생성을 자동화하려면 사용하는 워크플로우에 맞는 연동 문서에서 시작하세요. 여러 앱의 이벤트를 트리거로 타입캐스트 음성 생성을 실행하고 도구 사이의 작업 전달을 자동화합니다. 반복 가능한 TTS 파이프라인, 콘텐츠 워크플로우, 다국어 제작 시나리오를 시각적으로 구성합니다. 셀프호스트 또는 클라우드 자동화 워크플로우 안에서 타입캐스트를 하나의 단계로 호출합니다. 스프레드시트 행을 기준으로 배치 작업, 팀 운영, 콘텐츠 목록용 오디오를 생성합니다. ## 자주 찾는 링크 엔드포인트, 요청 파라미터, 응답 스키마, Try It 예시를 확인합니다. 사용할 수 있는 보이스를 살펴보고 요청에 넣을 Voice ID를 고릅니다. 출시하거나 확장하기 전에 플랜, 가격, 크레딧 사용량을 확인합니다. ssfm-v30과 ssfm-v21의 지원 언어, 감정 제어, 모델 특성을 비교합니다. ## 다음 단계 [퀵스타트](/ko/quickstart)에서 시작하거나, **어시스턴트에게 묻기**를 열고 앱, 사용 언어, 만들고 싶은 기능을 설명하세요. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 빠른 시작 ## 인증 시작하기 타입캐스트 API를 사용하려면 API 키로 요청을 인증해야 합니다. 다음 단계를 따르세요: [타입캐스트 API 콘솔](https://studio.typecast.ai/developers/api)을 방문하여 새 API 키를 생성하세요 API 키를 안전하게 보관하세요 - 환경 변수로 저장하는 것을 권장합니다 ## 첫 번째 요청 실행하기 Python SDK 0.4.0은 Python 3.10~3.14를 지원합니다. **Python 3.8, 3.9는 EOL로 인해 지원이 중단되었습니다.** 마지막 호환 SDK는 **typecast-python 0.3.15**입니다. 먼저 Python을 업그레이드하세요. [지원 범위와 이전 환경 안내](/ko/sdk/python)를 확인하세요. ```bash Python pip install --upgrade typecast-python ``` ```bash Javascript npm install @neosapience/typecast-js # pnpm add @neosapience/typecast-js # yarn add @neosapience/typecast-js ``` ```bash C#/.NET dotnet add package typecast-csharp ``` ```xml Java (Maven) com.neosapience typecast-java 1.2.12 ``` ```kotlin Kotlin (Gradle) dependencies { implementation("com.neosapience:typecast-kotlin:1.2.13") } ``` ```toml Rust (Cargo.toml) [dependencies] typecast-rust = "0.3.15" tokio = { version = "1", features = ["full"] } ``` ```bash Go go get github.com/neosapience/typecast-sdk/typecast-go ``` 모든 SDK는 최신 버전이 필요합니다. - **Python**: 이전 버전이 있다면 `pip install --upgrade typecast-python`으로 업그레이드하세요 - **Javascript**: 이전 버전이 있다면 `npm update @neosapience/typecast-js`로 업그레이드하세요 - **C#**: `dotnet add package typecast-csharp`로 업데이트하세요 - **Java**: `pom.xml` 또는 `build.gradle`에서 버전을 업데이트하세요 - **Kotlin**: `build.gradle.kts`에서 버전을 업데이트하세요 - **Rust**: `Cargo.toml`에서 버전을 업데이트하세요 음성 합성과 오디오 파일 저장만 필요하다면 SDK의 `generateToFile` 또는 `generate_to_file` 헬퍼를 사용하세요. 각 SDK 페이지에서 언어별 예시를 확인할 수 있습니다. ```python Python from typecast import Typecast from typecast.models import TTSRequest, SmartPrompt # 클라이언트 초기화 client = Typecast(api_key="YOUR_API_KEY") # 텍스트를 음성으로 변환 response = client.text_to_speech(TTSRequest( text="Everything is going to be okay.", model="ssfm-v30", voice_id="tc_672c5f5ce59fac2a48faeaee", prompt=SmartPrompt( emotion_type="smart", previous_text="I just got the best news!", next_text="I can't wait to celebrate!" ) )) # 오디오 파일 저장 with open('typecast.wav', 'wb') as f: f.write(response.audio_data) ``` ```javascript Javascript import { TypecastClient } from '@neosapience/typecast-js'; import fs from 'fs'; // 클라이언트 초기화 const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' }); // 텍스트를 음성으로 변환 const audio = await client.textToSpeech({ text: "Everything is going to be okay.", model: "ssfm-v30", voice_id: "tc_672c5f5ce59fac2a48faeaee", prompt: { emotion_type: "smart", previous_text: "I just got the best news!", next_text: "I can't wait to celebrate!" } }); // 오디오 파일 저장 await fs.promises.writeFile('typecast.wav', Buffer.from(audio.audioData)); ``` ```csharp C# using Typecast; using Typecast.Models; // 클라이언트 초기화 using var client = new TypecastClient("YOUR_API_KEY"); // 텍스트를 음성으로 변환 var request = new TTSRequest( text: "Everything is going to be okay.", voiceId: "tc_672c5f5ce59fac2a48faeaee", model: TTSModel.SsfmV30 ) { Prompt = new SmartPrompt( previousText: "I just got the best news!", nextText: "I can't wait to celebrate!" ) }; var response = await client.TextToSpeechAsync(request); // 오디오 파일 저장 await response.SaveToFileAsync("typecast.wav"); ``` ```java Java import com.neosapience.TypecastClient; import com.neosapience.models.*; import java.io.FileOutputStream; // 클라이언트 초기화 TypecastClient client = new TypecastClient("YOUR_API_KEY"); // 텍스트를 음성으로 변환 TTSRequest request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("Everything is going to be okay.") .model(TTSModel.SSFM_V30) .prompt(SmartPrompt.builder() .previousText("I just got the best news!") .nextText("I can't wait to celebrate!") .build()) .build(); TTSResponse response = client.textToSpeech(request); // 오디오 파일 저장 try (FileOutputStream fos = new FileOutputStream("typecast.wav")) { fos.write(response.getAudioData()); } client.close(); ``` ```kotlin Kotlin import com.neosapience.TypecastClient import com.neosapience.models.* import java.io.File // 클라이언트 초기화 val client = TypecastClient.create("YOUR_API_KEY") // 텍스트를 음성으로 변환 val request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("Everything is going to be okay.") .model(TTSModel.SSFM_V30) .prompt(SmartPrompt.builder() .previousText("I just got the best news!") .nextText("I can't wait to celebrate!") .build()) .build() val response = client.textToSpeech(request) // 오디오 파일 저장 File("typecast.wav").writeBytes(response.audioData) client.close() ``` ```rust Rust use typecast_rust::{TypecastClient, TTSRequest, TTSModel, SmartPrompt}; use std::fs; #[tokio::main] async fn main() -> Result<(), Box> { // 클라이언트 초기화 let client = TypecastClient::with_api_key("YOUR_API_KEY")?; // 텍스트를 음성으로 변환 let request = TTSRequest::new( "tc_672c5f5ce59fac2a48faeaee", "Everything is going to be okay.", TTSModel::SsfmV30, ) .prompt( SmartPrompt::new() .previous_text("I just got the best news!") .next_text("I can't wait to celebrate!") ); let response = client.text_to_speech(&request).await?; // 오디오 파일 저장 fs::write("typecast.wav", &response.audio_data)?; Ok(()) } ``` API 키를 두 가지 방법으로 설정할 수 있습니다: {/* - .env 파일에 추가 */} - 애플리케이션 코드에서 직접 구성 - 셸 환경 변수로 설정 ```bash Shell (Linux/macOS) # 현재 세션에 설정 export TYPECAST_API_KEY='YOUR_API_KEY' ``` ```bash Shell (Windows) # 현재 세션에 설정 set TYPECAST_API_KEY=YOUR_API_KEY ``` {/* ```bash .env TYPECAST_API_KEY = 'YOUR_API_KEY'; ``` */} ```python Python import requests import os api_key = os.environ.get("TYPECAST_API_KEY", "YOUR_API_KEY") url = "https://api.typecast.ai/v1/text-to-speech" headers = {"X-API-KEY": api_key, "Content-Type": "application/json"} payload = { "text": "Everything is going to be okay.", "model": "ssfm-v30", "voice_id": "tc_672c5f5ce59fac2a48faeaee", "prompt": { "emotion_type": "smart", "previous_text": "I just got the best news!", "next_text": "I can't wait to celebrate!" } } response = requests.post(url, headers=headers, json=payload) if response.status_code == 200: with open('typecast.wav', 'wb') as f: f.write(response.content) print("Audio file saved as typecast.wav") else: print(f"Error: {response.status_code} - {response.text}") ``` ```javascript Javascript import fs from "fs"; const apiKey = process.env.TYPECAST_API_KEY || 'YOUR_API_KEY'; async function convertTextToSpeech() { const url = 'https://api.typecast.ai/v1/text-to-speech'; const payload = { text: "Everything is going to be okay.", model: "ssfm-v30", voice_id: "tc_672c5f5ce59fac2a48faeaee", prompt: { emotion_type: "smart", previous_text: "I just got the best news!", next_text: "I can't wait to celebrate!" } }; try { const response = await fetch(url, { method: 'POST', headers: { 'X-API-KEY': apiKey, 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (!response.ok) { throw new Error(`Error: ${response.status} - ${await response.text()}`); } const audioData = await response.arrayBuffer(); fs.writeFileSync('typecast.wav', Buffer.from(audioData)); console.log('Audio file saved as typecast.wav'); } catch (error) { console.error('Error converting text to speech:', error); } } convertTextToSpeech(); ``` ```java Java import java.io.*; import java.net.HttpURLConnection; import java.net.URL; String apiKey = System.getenv("TYPECAST_API_KEY"); if (apiKey == null) apiKey = "YOUR_API_KEY"; String urlString = "https://api.typecast.ai/v1/text-to-speech"; String payload = "{" + "\"text\": \"Everything is going to be okay.\"," + "\"model\": \"ssfm-v30\"," + "\"voice_id\": \"tc_672c5f5ce59fac2a48faeaee\"," + "\"prompt\": {" + "\"emotion_type\": \"smart\"," + "\"previous_text\": \"I just got the best news!\"," + "\"next_text\": \"I can't wait to celebrate!\"" + "}" + "}"; URL url = new URL(urlString); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("X-API-KEY", apiKey); conn.setRequestProperty("Content-Type", "application/json"); conn.setDoOutput(true); try (OutputStream os = conn.getOutputStream()) { os.write(payload.getBytes("UTF-8")); } if (conn.getResponseCode() == 200) { try (InputStream is = conn.getInputStream(); FileOutputStream fos = new FileOutputStream("typecast.wav")) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = is.read(buffer)) != -1) { fos.write(buffer, 0, bytesRead); } } System.out.println("Audio file saved as typecast.wav"); } else { System.out.println("Error: " + conn.getResponseCode()); } ``` ```csharp C# using System.Net.Http.Headers; using System.Text; using System.Text.Json; var apiKey = Environment.GetEnvironmentVariable("TYPECAST_API_KEY") ?? "YOUR_API_KEY"; using var client = new HttpClient(); client.DefaultRequestHeaders.Add("X-API-KEY", apiKey); var payload = new { text = "Everything is going to be okay.", model = "ssfm-v30", voice_id = "tc_672c5f5ce59fac2a48faeaee", prompt = new { emotion_type = "smart", previous_text = "I just got the best news!", next_text = "I can't wait to celebrate!" } }; var content = new StringContent( JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json" ); var response = await client.PostAsync("https://api.typecast.ai/v1/text-to-speech", content); if (response.IsSuccessStatusCode) { var audioData = await response.Content.ReadAsByteArrayAsync(); await File.WriteAllBytesAsync("typecast.wav", audioData); Console.WriteLine("Audio file saved as typecast.wav"); } else { Console.WriteLine($"Error: {response.StatusCode}"); } ``` ```bash cURL curl -X POST "https://api.typecast.ai/v1/text-to-speech" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "ssfm-v30", "text": "Everything is going to be okay.", "voice_id": "tc_672c5f5ce59fac2a48faeaee", "prompt": { "emotion_type": "smart", "previous_text": "I just got the best news!", "next_text": "I can'\''t wait to celebrate!" } }' --output typecast.wav ``` ### 오디오 출력 설정 요청에 `output` 객체를 추가하여 오디오 출력을 커스터마이즈할 수 있습니다: | 파라미터 | 타입 | 범위 | 기본값 | 설명 | |---------|------|------|--------|------| | `volume` | integer | 0–200 | 100 | 상대적 볼륨 스케일링. `target_lufs`와 동시에 사용할 수 없습니다. | | `target_lufs` | number | -70–0 | - | LUFS 기반 절대 라우드니스 정규화. `volume`과 동시에 사용할 수 없습니다. | | `audio_pitch` | integer | -12–12 | 0 | 피치 조정 (반음 단위). | | `audio_tempo` | number | 0.5–2.0 | 1.0 | 재생 속도 배율. | | `audio_format` | string | wav, mp3 | wav | 출력 오디오 포맷. | | `remove_silence_ms` | integer / null | 0–1000 | null | 남길 무음 길이(ms). 0은 검출된 무음 제거, 생략/null은 비활성화. | `target_lufs`는 여러 클립 간 일관된 라우드니스가 필요할 때, `volume`은 단순한 상대적 볼륨 조절이 필요할 때 사용하세요. ```json 예시: target_lufs를 사용한 출력 설정 { "text": "일관된 라우드니스 예시입니다.", "model": "ssfm-v30", "voice_id": "tc_672c5f5ce59fac2a48faeaee", "output": { "target_lufs": -14.0, "audio_format": "mp3" } } ``` 요청에 사용할 수 있는 Voice ID를 찾아보려면 API 레퍼런스의 [보이스 목록 조회](/docs/ko/api-reference/voices/list-voices)를 참조하세요. ## 모든 보이스 목록 조회하기 타입캐스트를 효과적으로 사용하려면 Voice ID에 액세스해야 합니다. 최신 `/v3/voices` 엔드포인트는 다국어 이름, 지원 모델, 감정, 미리듣기 메타데이터가 포함된 보이스 목록을 제공합니다. 모델, 성별, 연령대 및 사용 사례 등의 선택적 쿼리 파라미터를 사용하여 보이스를 필터링할 수 있습니다. [보이스](https://studio.typecast.ai/developers/api/voices) 페이지에서 API 호출 없이 API에서 사용할 수 있는 보이스 목록과 샘플 음성을 미리 보고 들어볼 수 있습니다. 먼저 보이스를 비교한 뒤, 사용할 Voice ID를 API 요청에 넣어주세요. ```python Python from typecast import Typecast from typecast.models import VoicesV2Filter, TTSModel # 클라이언트 초기화 client = Typecast(api_key="YOUR_API_KEY") # 모든 음성 가져오기 (선택적으로 모델, 성별, 나이, 사용 사례로 필터링) voices = client.voices_v3(VoicesV2Filter(model=TTSModel.SSFM_V30)) print(f"Found {len(voices)} voices:") for voice in voices: for model in voice.models: print(f"ID: {voice.voice_id}, Name: {voice.voice_name.kor}, Model: {model.version.value}, Emotions: {', '.join(model.emotions)}") ``` ```javascript Javascript import { TypecastClient } from '@neosapience/typecast-js'; // 클라이언트 초기화 const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' }); // 모든 음성 가져오기 (선택적으로 모델, 성별, 나이, 사용 사례로 필터링) const voices = await client.getVoicesV3({model: 'ssfm-v30'}); console.log(`Found ${voices.length} voices:`); voices.forEach(voice => { voice.models.forEach(model => { console.log(`ID: ${voice.voice_id}, Name: ${voice.voice_name.kor}, Model: ${model.version}, Emotions: ${model.emotions.join(', ')}`); }); }); ``` ```csharp C# using Typecast; using Typecast.Models; // 클라이언트 초기화 using var client = new TypecastClient("YOUR_API_KEY"); // 모든 음성 가져오기 (선택적으로 모델, 성별, 나이, 사용 사례로 필터링) var filter = new VoicesV2Filter { Model = TTSModel.SsfmV30 }; var voices = await client.GetVoicesV3Async(filter); Console.WriteLine($"Found {voices.Count} voices:"); foreach (var voice in voices) { foreach (var model in voice.Models) { Console.WriteLine($"ID: {voice.VoiceId}, Name: {voice.VoiceName.Kor}, Model: {model.Version}, Emotions: {string.Join(", ", model.Emotions)}"); } } ``` ```java Java import com.neosapience.TypecastClient; import com.neosapience.models.*; // 클라이언트 초기화 TypecastClient client = new TypecastClient("YOUR_API_KEY"); // 모든 음성 가져오기 (선택적으로 모델, 성별, 나이, 사용 사례로 필터링) VoicesV2Filter filter = VoicesV2Filter.builder() .model(TTSModel.SSFM_V30) .build(); List voices = client.getVoicesV3(filter); System.out.println("Found " + voices.size() + " voices:"); for (VoiceV3Response voice : voices) { for (ModelInfo model : voice.getModels()) { System.out.println("ID: " + voice.getVoiceId() + ", Name: " + voice.getVoiceName().kor + ", Model: " + model.getVersion() + ", Emotions: " + String.join(", ", model.getEmotions())); } } client.close(); ``` ```python Python import requests import os api_key = os.environ.get("TYPECAST_API_KEY", "YOUR_API_KEY") url = "https://api.typecast.ai/v3/voices" headers = {"X-API-KEY": api_key} params = {"model": "ssfm-v30"} # 선택 사항: model, gender, age, use_cases response = requests.get(url, headers=headers, params=params) if response.status_code == 200: voices = response.json() print(f"Found {len(voices)} voices:") for voice in voices: for model in voice['models']: print(f"ID: {voice['voice_id']}, Name: {voice['voice_name']['kor']}, Model: {model['version']}, Emotions: {', '.join(model['emotions'])}") else: print(f"Error: {response.status_code} - {response.text}") ``` ```javascript Javascript const apiKey = process.env.TYPECAST_API_KEY || 'YOUR_API_KEY'; async function getVoices() { const url = 'https://api.typecast.ai/v3/voices'; const params = new URLSearchParams({model: 'ssfm-v30'}); // 선택 사항: model, gender, age, use_cases try { const response = await fetch(`${url}?${params}`, { method: 'GET', headers: {'X-API-KEY': apiKey} }); if (!response.ok) { throw new Error(`Error: ${response.status} - ${await response.text()}`); } const voices = await response.json(); console.log(`Found ${voices.length} voices:`); voices.forEach(voice => { voice.models.forEach(model => { console.log(`ID: ${voice.voice_id}, Name: ${voice.voice_name.kor}, Model: ${model.version}, Emotions: ${model.emotions.join(', ')}`); }); }); } catch (error) { console.error('Error fetching voices:', error); } } getVoices(); ``` ```java Java import java.io.*; import java.net.HttpURLConnection; import java.net.URL; String apiKey = System.getenv("TYPECAST_API_KEY"); if (apiKey == null) apiKey = "YOUR_API_KEY"; String urlString = "https://api.typecast.ai/v3/voices?model=ssfm-v30"; URL url = new URL(urlString); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("X-API-KEY", apiKey); if (conn.getResponseCode() == 200) { try (BufferedReader br = new BufferedReader( new InputStreamReader(conn.getInputStream(), "UTF-8"))) { StringBuilder response = new StringBuilder(); String line; while ((line = br.readLine()) != null) { response.append(line); } System.out.println(response.toString()); } } else { System.out.println("Error: " + conn.getResponseCode()); } ``` ```csharp C# var apiKey = Environment.GetEnvironmentVariable("TYPECAST_API_KEY") ?? "YOUR_API_KEY"; using var client = new HttpClient(); client.DefaultRequestHeaders.Add("X-API-KEY", apiKey); var response = await client.GetAsync("https://api.typecast.ai/v3/voices?model=ssfm-v30"); if (response.IsSuccessStatusCode) { var json = await response.Content.ReadAsStringAsync(); Console.WriteLine(json); } else { Console.WriteLine($"Error: {response.StatusCode}"); } ``` ```bash cURL curl -X GET "https://api.typecast.ai/v3/voices?model=ssfm-v30" \ -H "X-API-KEY: YOUR_API_KEY" ``` 응답은 각각 다음을 포함하는 음성 객체의 JSON 배열입니다: ```json { "voice_id": "tc_672c5f5ce59fac2a48faeaee", "voice_name": {"eng": "Dylan", "kor": "딜런"}, "models": [ { "version": "ssfm-v30", "emotions": ["normal", "happy", "sad", "angry", "whisper", "toneup", "tonedown"] } ], "gender": "male", "age": "young_adult", "use_cases": ["Conversational", "TikTok/Reels/Shorts", "Audiobook/Storytelling"], "voice_type": "original", "preview_url": "https://..." } ``` 텍스트 음성 변환 요청을 할 때 유효한 Voice ID가 필요합니다. ssfm-v30을 사용하면 모든 7가지 감정 프리셋을 모든 보이스에서 사용할 수 있습니다. ## 실시간 오디오 스트리밍 저지연 애플리케이션의 경우, 스트리밍 엔드포인트를 사용하여 전체 합성을 기다리지 않고 오디오 청크가 도착하는 즉시 재생할 수 있습니다. **WAV 스트리밍 형식:** 32000 Hz, 16비트, 모노 PCM. 첫 번째 청크에 44바이트 WAV 헤더가 포함되며, 이후 청크는 원시 PCM 데이터만 포함합니다. ```python Python # pip install typecast-python sounddevice import sounddevice as sd from typecast import Typecast from typecast.models import TTSRequestStream, OutputStream client = Typecast(api_key="YOUR_API_KEY") request = TTSRequestStream( text="이 텍스트를 실시간으로 오디오로 스트리밍합니다.", model="ssfm-v30", voice_id="tc_672c5f5ce59fac2a48faeaee", output=OutputStream(audio_format="wav", target_lufs=-14.0) ) with sd.RawOutputStream(samplerate=32000, channels=1, dtype="int16") as player: buf, first = bytearray(), True for chunk in client.text_to_speech_stream(request): if first: chunk = chunk[44:] # 44바이트 WAV 헤더 건너뛰기 first = False buf.extend(chunk) n = len(buf) - (len(buf) % 2) # int16 정렬 if n: player.write(bytes(buf[:n])) del buf[:n] ``` ```javascript Javascript // Node 18+. 스트림을 ffplay로 파이핑하여 실시간 재생. // 사전 설치: ffmpeg (brew/choco/apt install ffmpeg) import { spawn } from "node:child_process"; import { TypecastClient } from '@neosapience/typecast-js'; const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' }); const ffplay = spawn( "ffplay", ["-autoexit", "-nodisp", "-loglevel", "error", "-i", "pipe:0"], { stdio: ["pipe", "ignore", "ignore"] }, ); const stream = await client.textToSpeechStream({ text: "이 텍스트를 실시간으로 오디오로 스트리밍합니다.", model: "ssfm-v30", voice_id: "tc_672c5f5ce59fac2a48faeaee", output: { audio_format: "wav", target_lufs: -14.0 } }); const reader = stream.getReader(); while (true) { const { value, done } = await reader.read(); if (done) break; ffplay.stdin.write(value); } ffplay.stdin.end(); await new Promise((resolve) => ffplay.on("close", resolve)); ``` ```java Java import com.neosapience.TypecastClient; import com.neosapience.models.*; import javax.sound.sampled.*; import java.io.*; TypecastClient client = new TypecastClient("YOUR_API_KEY"); TTSRequestStream request = TTSRequestStream.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("이 텍스트를 실시간으로 오디오로 스트리밍합니다.") .model(TTSModel.SSFM_V30) .output(com.neosapience.models.OutputStream.builder() .audioFormat(AudioFormat.WAV) .targetLufs(-14.0).build()) .build(); // 32000 Hz, 16비트, 모노, signed, 리틀엔디안 javax.sound.sampled.AudioFormat format = new javax.sound.sampled.AudioFormat(32000, 16, 1, true, false); SourceDataLine line = javax.sound.sampled.AudioSystem.getSourceDataLine(format); line.open(format, 8192); line.start(); try (InputStream stream = client.textToSpeechStream(request)) { byte[] buf = new byte[4096]; boolean first = true; int bytesRead; while ((bytesRead = stream.read(buf)) != -1) { int offset = 0; if (first) { offset = 44; bytesRead -= 44; first = false; } line.write(buf, offset, bytesRead); } } line.drain(); line.close(); client.close(); ``` 더 많은 언어(Go, Rust, Swift, C#, Kotlin, C)의 실시간 재생 예시는 각 [SDK 문서](/ko/sdk/python)를 참조하세요. ```python Python # pip install requests sounddevice import requests import sounddevice as sd import os api_key = os.environ.get("TYPECAST_API_KEY", "YOUR_API_KEY") response = requests.post( "https://api.typecast.ai/v1/text-to-speech/stream", headers={"X-API-KEY": api_key, "Content-Type": "application/json"}, json={ "text": "이 텍스트를 실시간으로 오디오로 스트리밍합니다.", "model": "ssfm-v30", "voice_id": "tc_672c5f5ce59fac2a48faeaee", "output": {"audio_format": "wav", "target_lufs": -14.0}, }, stream=True ) response.raise_for_status() with sd.RawOutputStream(samplerate=32000, channels=1, dtype="int16") as player: buf, first = bytearray(), True for chunk in response.iter_content(chunk_size=4096): if not chunk: continue if first: chunk = chunk[44:] # WAV 헤더 건너뛰기 first = False buf.extend(chunk) n = len(buf) - (len(buf) % 2) if n: player.write(bytes(buf[:n])) del buf[:n] ``` ```bash cURL + ffplay # ffplay로 직접 파이핑하여 즉시 재생 curl -s -X POST "https://api.typecast.ai/v1/text-to-speech/stream" \ -H "X-API-KEY: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "ssfm-v30", "text": "이 텍스트를 실시간으로 오디오로 스트리밍합니다.", "voice_id": "tc_672c5f5ce59fac2a48faeaee", "output": {"audio_format": "wav", "target_lufs": -14.0} }' | ffplay -autoexit -nodisp -loglevel error -i pipe:0 ``` | 파라미터 | 타입 | 범위 | 기본값 | 설명 | |---------|------|------|--------|------| | `audio_pitch` | integer | -12–12 | 0 | 세미톤 단위의 피치 조절 | | `audio_tempo` | number | 0.5–2.0 | 1.0 | 속도 배율 | | `audio_format` | string | wav, mp3 | wav | 출력 오디오 형식 | | `remove_silence_ms` | integer / null | 0–1000 | null | 남길 무음 길이(ms). 0은 검출된 무음 제거, 생략/null은 비활성화. | | `target_lufs` | number | -70–0 | - | LUFS 기반 절대 라우드니스 정규화 | `target_lufs`로 스트리밍 오디오의 라우드니스를 클립 간 일관되게 맞출 수 있습니다. 스트리밍 모드에서는 `volume`을 지원하지 않습니다. ## 타임스탬프 TTS로 자막 생성하기 `POST /v1/text-to-speech/with-timestamps`를 사용하면 오디오와 함께 단어 단위 정렬 데이터를 받아 자막, 가라오케, 립싱크 애플리케이션을 만들 수 있습니다. ```python Python from typecast import Typecast from typecast.models import TTSRequestWithTimestamps client = Typecast(api_key="YOUR_API_KEY") response = client.text_to_speech_with_timestamps(TTSRequestWithTimestamps( text="Hello. How are you?", model="ssfm-v30", voice_id="tc_60e5426de8b95f1d3000d7b5", )) # 단어별 타임스탬프 출력 for word in response.words: print(f"[{word.start_time:.3f}s – {word.end_time:.3f}s] {word.text}") # SRT 자막 내보내기 srt = response.to_srt() with open("output.srt", "w") as f: f.write(srt) ``` ```typescript JavaScript import Typecast from "@neosapience/typecast-js"; const client = new Typecast({ apiKey: "YOUR_API_KEY" }); const response = await client.textToSpeechWithTimestamps({ voiceId: "tc_60e5426de8b95f1d3000d7b5", text: "Hello. How are you?", model: "ssfm-v30", }); // 단어별 타임스탬프 출력 for (const word of response.words) { console.log(`[${word.startTime.toFixed(3)}s – ${word.endTime.toFixed(3)}s] ${word.text}`); } // VTT 자막 내보내기 const vtt = response.toVtt(); ``` ```bash cURL curl -X POST https://api.typecast.ai/v1/text-to-speech/with-timestamps \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "voice_id": "tc_60e5426de8b95f1d3000d7b5", "text": "Hello. How are you?", "model": "ssfm-v30", "granularity": "word" }' ``` 각 언어별 타임스탬프 TTS 사용법은 [SDK 문서](/ko/sdk)를 참조하세요. 일본어(jpn), 중국어(zho)는 `granularity: "char"` (문자 단위) 를 사용해야 합니다. ## 다음 단계 축하합니다! 첫 번째 AI 음성을 만들었습니다. 더 자세히 알아보려면 다음 리소스를 참조하세요: 타입캐스트 API 사용 방법 알아보기 ssfm-v30 및 ssfm-v21 모델에 대해 알아보기 최신 API 변경 사항 및 업데이트 확인 ## 무음 길이 조절 `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. 일반·스트리밍·타임스탬프 TTS는 `output.remove_silence_ms`, Compose는 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 전달합니다. 반환 타임스탬프는 처리 후 오디오를 기준으로 하며, 명시적인 `pause` 세그먼트는 유지됩니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. ```json { "output": { "remove_silence_ms": 300 } } ``` SDK별 최소 지원 버전은 [SDK 개요](/docs/ko/sdk/overview)를 참조하세요. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 모델 ## 파운데이션 모델 SSFM 소개 타입캐스트는 현재 차세대 텍스트 음성 변환 기술인 타입캐스트 음성 합성 파운데이션 모델(Typecast Speech Synthesis Foundation Model, 약칭 Typecast SSFM)이라는 고급 AI 음성 모델을 사용합니다. 이 모델은 텍스트를 비교할 수 없는 자연스러움과 표현력으로 생생하게 변환합니다. ## 모델 개요 | 모델 | 출시일 | 설명 | | :------- | :----------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ssfm-v30 | 2026.01 | - 더 자연스러운 음성과 부드러운 운율 및 속도
- 7가지 감정 프리셋으로 감정 제어
- 37개 언어 지원
- 스마트 이모션 사용 가능 | | ssfm-v21 | 2025.07 | - 낮은 지연 시간
- 4가지 감정 프리셋으로 감정 제어
- 27개 언어 지원 | ## ssfm-v30 - 스마트 이모션: 텍스트 맥락에서 적절한 감정을 자동으로 감지하여 음성에 적용합니다. - 감정 프리셋: `normal`, `happy`, `sad`, `angry`, `whisper`, `toneup`, `tonedown` (모든 음성에서 사용 가능) - 지원 언어: 영어, 한국어, 아랍어, 벵골어, 불가리아어, 광둥어, 중국어(만다린), 크로아티아어, 체코어, 덴마크어, 네덜란드어, 핀란드어, 프랑스어, 독일어, 그리스어, 힌디어, 헝가리어, 인도네시아어, 이탈리아어, 일본어, 말레이어, 민난어, 노르웨이어, 폴란드어, 포르투갈어, 펀자브어, 루마니아어, 러시아어, 슬로바키아어, 스페인어, 스웨덴어, 타갈로그어, 타밀어, 태국어, 터키어, 우크라이나어, 베트남어 ## ssfm-v21 - 감정 프리셋: `normal`, `happy`, `sad`, `angry` (음성에 따라 사용 가능 여부가 다름) - 지원 언어: 영어, 한국어, 아랍어, 불가리아어, 중국어, 크로아티아어, 체코어, 덴마크어, 네덜란드어, 핀란드어, 프랑스어, 독일어, 그리스어, 인도네시아어, 이탈리아어, 일본어, 말레이어, 폴란드어, 포르투갈어, 루마니아어, 러시아어, 슬로바키아어, 스페인어, 스웨덴어, 타갈로그어, 타밀어, 우크라이나어 --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 변경 로그 공식 SDK 13종과 Cast v1.0.10, n8n 1.2.5, Zapier 2.2.7, Pipecat 0.3.1, 호스팅 Typecast API MCP에 `remove_silence_ms` 지원이 반영되었습니다. SDK별 최소 버전은 [SDK 개요](/docs/ko/sdk/overview)를 참조하세요. `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. 일반·스트리밍·타임스탬프 TTS는 `output.remove_silence_ms`, Compose는 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 전달합니다. 반환 타임스탬프는 처리 후 오디오를 기준으로 하며, 명시적인 `pause` 세그먼트는 유지됩니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. TEN과 LlamaIndex의 해당 연동 변경은 upstream PR 검토 중이며, 이번 배포 완료 목록에 포함되지 않습니다. ### 새 파라미터: remove\_silence\_ms 생성된 음성에서 검출된 무음 구간을 줄이는 `remove_silence_ms`가 추가되었습니다. 제거할 시간이 아니라 남길 무음 길이를 밀리초(ms) 단위로 지정합니다. * **입력값:** `0`부터 `1000`ms까지의 정수입니다. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. * **TTS·스트리밍 TTS·타임스탬프 TTS:** `output.remove_silence_ms`로 설정합니다. 반환되는 타임스탬프는 무음 제거 후 오디오를 기준으로 합니다. * **조합형 TTS:** 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 설정합니다. 명시적으로 추가한 `pause` 세그먼트는 유지됩니다. 자세한 내용은 [TTS](/docs/ko/api-reference/text-to-speech/text-to-speech), [스트리밍 TTS](/docs/ko/api-reference/text-to-speech/streaming-text-to-speech), [타임스탬프 TTS](/docs/ko/api-reference/text-to-speech/text-to-speech-with-timestamps), [조합형 TTS](/docs/ko/api-reference/text-to-speech/compose-text-to-speech) API 레퍼런스를 참조하세요. ### 새 엔드포인트: POST /v1/custom-voices/professional-clone 비동기 방식의 프리미엄 보이스 클로닝이 추가되었습니다. 언어 정보와 WAV 또는 MP3 샘플을 제출한 뒤, 반환된 커스텀 보이스의 상태가 `completed` 또는 `failed`가 될 때까지 확인할 수 있습니다. V3 보이스·커스텀 보이스 API와 공식 SDK 패키지, Cast CLI도 함께 업데이트되었습니다. 주요 최신 릴리스는 Python 0.3.14, JavaScript 0.4.12, Go 0.3.13, Rust 0.3.13, 프리미엄 클로닝을 지원하는 Cast CLI v1.0.9입니다. ### 새 엔드포인트: GET /v1/voices/recommendations 자연어 설명을 바탕으로 적합한 보이스를 추천하는 기능이 추가되었습니다. ### 새 엔드포인트: POST /v1/text-to-speech/compose 여러 텍스트 구간과 구간별 보이스·음성 설정을 한 요청으로 합성하는 Compose TTS가 추가되었습니다. ### 새 엔드포인트: POST /v1/voices/clone WAV 또는 MP3 샘플을 사용한 퀵 보이스 클로닝이 추가되었습니다. 기존 엔드포인트는 현재 지원 중단되었으며, 신규 연동에는 `POST /v1/custom-voices/instant-clone`을 사용하세요. ### 새 엔드포인트: POST /v1/text-to-speech/with-timestamps 단일 응답으로 합성된 오디오와 단어·문자 단위 정렬 데이터를 함께 반환합니다. 자동 자막, 가라오케 하이라이트, 립싱크 애니메이션 등에 활용하기 좋습니다. ```text POST /v1/text-to-speech/with-timestamps ``` **요청 스키마:** ```json { "voice_id": "tc_60e5426de8b95f1d3000d7b5", "text": "안녕?", "model": "ssfm-v30" } ``` **응답 스키마 (요약):** ```json { "audio": "", "audio_format": "wav", "audio_duration": 0.52, "words": [ { "text": "안녕?", "start": 0.0, "end": 0.52 } ], "characters": [ { "text": "안", "start": 0.0, "end": 0.2 }, { "text": "녕", "start": 0.2, "end": 0.45 }, { "text": "?", "start": 0.45, "end": 0.52 } ] } ``` **`granularity` 파라미터:** `granularity`는 선택 파라미터이며, 생략하면 단어 단위 정렬과 문자 단위 정렬을 함께 응답합니다. | 값 | 설명 | | :----- | :----------------------------------------- | | `word` | 단어 단위 정렬. 공백이 있는 언어에 권장됩니다. | | `char` | 문자 단위 정렬. 일본어(`jpn`) 및 중국어(`zho`)에는 필수입니다. | **캡션 분할 규칙:** 문장 종결 기호(`. ? ! 。 ? !`)에서 분할하며, 큐당 7초/42자 상한선을 적용합니다(BBC/Netflix 자막 가이드라인). ### SDK 업데이트 - 타임스탬프 TTS 전 11개 SDK에 추가 | SDK | 버전 | 메서드 | | :--------- | :----- | :------------------------------------------ | | Python | 0.3.0 | `text_to_speech_with_timestamps()` | | JavaScript | 0.4.0 | `textToSpeechWithTimestamps()` | | Go | v0.3.0 | `TextToSpeechWithTimestamps()` | | Rust | 0.3.0 | `text_to_speech_with_timestamps()` | | Swift | v0.3.0 | `textToSpeechWithTimestamps()` | | C# | 0.3.0 | `TextToSpeechWithTimestampsAsync()` | | Java | 1.2.0 | `textToSpeechWithTimestamps()` | | Kotlin | 1.2.0 | `textToSpeechWithTimestamps()` | | C | 1.2.0 | `typecast_text_to_speech_with_timestamps()` | | Zig | v0.2.0 | `textToSpeechWithTimestamps()` | | PHP | v0.1.0 | `textToSpeechWithTimestamps()` | 모든 SDK 응답 객체에는 `toSrt()` / `toVtt()` 자막 내보내기 헬퍼와 `saveAudio(path)` / `audio_bytes()` 편의 메서드가 포함되어 있습니다. ### 새 엔드포인트: POST /v1/text-to-speech/stream 전체 합성을 기다리지 않고 생성되는 오디오 청크를 실시간으로 전달하는 저지연 스트리밍 엔드포인트가 추가되었습니다. ```text POST /v1/text-to-speech/stream ``` **`/v1/text-to-speech`와의 주요 차이점:** | 기능 | 표준 | 스트리밍 | | :------------ | :--------- | :---------------------------------------- | | 응답 | 완성된 오디오 파일 | 청크 오디오 스트림 | | 지연 시간 | 전체 합성 대기 | 첫 청크 \~200ms | | `volume` | 지원 | 미지원 | | `target_lufs` | 지원 | 지원 | | 출력 설정 | `Output` | `OutputStream` (피치, 템포, 포맷, target\_lufs) | **요청 스키마:** ```json { "voice_id": "tc_xxxxx", "text": "문의해 주셔서 감사합니다. 금요일 오후 7시로 예약이 확정되었습니다.", "model": "ssfm-v30", "language": "kor", "output": { "audio_pitch": 0, "audio_tempo": 1.0, "audio_format": "wav" } } ``` **응답:** 청크 바이너리 스트림 (`audio/wav` 또는 `audio/mpeg`). ### 새 엔드포인트: GET /v1/users/me/subscription 인증된 사용자의 플랜 등급, 크레딧 사용량, 동시 요청 제한을 조회합니다. ```text GET /v1/users/me/subscription ``` **응답 스키마:** ```json { "plan": "lite", "credits": { "plan_credits": 100000, "used_credits": 157300 }, "limits": { "concurrency_limit": 5 } } ``` ### SDK 업데이트 9개 공식 SDK 모두 스트리밍 및 구독 지원이 추가되었습니다: | SDK | 버전 | 스트리밍 메서드 | | :--------- | :----- | :--------------------------------------------- | | Python | 0.2.0 | `text_to_speech_stream()` (동기 + 비동기) | | JavaScript | 0.3.0 | `textToSpeechStream()` → `ReadableStream` | | Go | v0.2.0 | `TextToSpeechStream()` → `io.ReadCloser` | | Rust | 0.2.0 | `text_to_speech_stream()` → `Stream` | | Swift | v0.2.0 | `textToSpeechStream()` → `AsyncThrowingStream` | | C# | 0.2.0 | `TextToSpeechStreamAsync()` → `Stream` | | Java | 1.1.0 | `textToSpeechStream()` → `InputStream` | | Kotlin | 1.1.0 | `textToSpeechStream()` → `InputStream` | | C | 1.1.0 | `typecast_text_to_speech_stream()` (콜백) | ### 새 모델: ssfm-v30 음성 품질이 개선되고 기능이 확장된 새로운 `ssfm-v30` 모델 지원이 추가되었습니다. **새로운 기능:** * **스마트 이모션** - `SmartPrompt`를 사용한 문맥 인식 감정 추론 * **7가지 감정 프리셋** - `whisper`, `toneup`, `tonedown` 프리셋 추가 * **범용 감정 지원** - 모든 감정을 모든 보이스에서 사용 가능 * **37개 언어** - 10개 새 언어 추가 **새로 추가된 언어:** 벵골어, 광둥어, 힌디어, 헝가리어, 민난어, 노르웨이어, 펀자브어, 태국어, 터키어, 베트남어 **요청 스키마 변경:** ```json // ssfm-v30 SmartPrompt 사용 (문맥 인식 감정) { "model": "ssfm-v30", "prompt": { "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!", "next_text": "I am literally bursting with happiness and I never want this feeling to end!" } } // ssfm-v30 PresetPrompt 사용 (수동 감정 선택) { "model": "ssfm-v30", "prompt": { "emotion_type": "preset", "emotion_preset": "happy", "emotion_intensity": 1.0 } } ``` ### 새 엔드포인트: GET /v2/voices 모델별로 그룹화된 감정과 추가 메타데이터가 포함된 향상된 보이스 목록 엔드포인트가 추가되었습니다. ```text GET /v2/voices ``` **쿼리 파라미터:** | 파라미터 | 타입 | 설명 | | :---------- | :----- | :------------------------------------------------------------------- | | `model` | string | 모델별 필터링 (`ssfm-v21`, `ssfm-v30`) | | `gender` | string | 성별별 필터링 (`male`, `female`) | | `age` | string | 연령대별 필터링 (`child`, `teenager`, `young_adult`, `middle_age`, `elder`) | | `use_cases` | string | 사용 사례별 필터링 (`Audiobook`, `Game`, `E-learning` 등) | **응답 스키마:** ```json [ { "voice_id": "tc_xxxxx", "voice_name": "음성 이름", "models": [ { "version": "ssfm-v30", "emotions": ["normal", "happy", "sad", "angry", "whisper", "toneup", "tonedown"] }, { "version": "ssfm-v21", "emotions": ["normal", "happy", "sad"] } ], "gender": "female", "age": "young_adult", "use_cases": ["Audiobook", "E-learning"] } ] ``` ### 지원 중단: 보이스 관리 엔드포인트 다음 엔드포인트가 지원 중단되어 제거되었습니다: | 엔드포인트 | 상태 | | :-------------------------- | :-- | | `POST /v1/voices` | 제거됨 | | `GET /v1/voices/{voice_id}` | 제거됨 | 향상된 메타데이터가 포함된 보이스 목록을 보려면 `GET /v2/voices`를 사용하세요. ### 초기 출시: ssfm-v21 `ssfm-v21` 모델로 타입캐스트 Text-to-Speech API를 출시했습니다. **엔드포인트:** | 메서드 | 엔드포인트 | 설명 | | :--- | :------------------- | :----------- | | POST | `/v1/text-to-speech` | 텍스트에서 음성 생성 | | GET | `/v1/voices` | 사용 가능한 음성 목록 | **기능:** * 저지연 음성 합성 * 4가지 감정 프리셋: `normal`, `happy`, `sad`, `angry` * 음성에 따라 감정 사용 가능 여부가 다름 * 27개 언어 지원 **지원 언어:** 영어, 한국어, 아랍어, 불가리아어, 중국어, 크로아티아어, 체코어, 덴마크어, 네덜란드어, 핀란드어, 프랑스어, 독일어, 그리스어, 인도네시아어, 이탈리아어, 일본어, 말레이어, 폴란드어, 포르투갈어, 루마니아어, 러시아어, 슬로바키아어, 스페인어, 스웨덴어, 타갈로그어, 타밀어, 우크라이나어 **요청 스키마:** ```json { "voice_id": "tc_xxxxx", "text": "Everything is so incredibly perfect that I feel like I'm dreaming.", "model": "ssfm-v21", "language": "kor", "prompt": { "emotion_preset": "normal", "emotion_intensity": 1.0 }, "output": { "volume": 100, "audio_pitch": 0, "audio_tempo": 1.0, "audio_format": "wav" } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # SDK 가이드 ## SDK를 써야 하는 이유 HTTP API를 직접 호출해도 타입캐스트 API를 사용할 수 있지만, 운영 코드에서는 SDK 사용을 권장합니다. SDK는 인증 헤더 구성, 요청/응답 타입, 에러 처리, 스트리밍 처리, 응답 파싱 같은 반복 작업을 감싸주기 때문에 직접 HTTP 호출을 구현하는 것보다 더 안전하고 빠르게 연동할 수 있습니다. 또한 **보이스 추천**, **텍스트 쉼 표현**, **다중 화자 합성**처럼 Direct API만으로 직접 구현하기 번거로운 고수준 워크플로우를 편리하게 제공합니다. 타입캐스트 API 연동 코드가 실행될 환경에 맞춰 SDK를 선택하세요. 스크립트나 프로토타입부터 시작한다면 Python으로 시작하기 쉽습니다. 운영 서비스에서는 이미 사용하는 백엔드나 앱 런타임에 맞는 SDK를 고르는 것이 좋습니다. | SDK | 이런 경우에 사용하세요 | | --- | --- | | [Python](/ko/sdk/python) | 스크립트, 노트북, 데이터 파이프라인, 백엔드 배치 작업, 빠른 API 프로토타입을 만들 때 | | [Javascript/Typescript](/ko/sdk/javascript) | Node.js 서비스, 프론트엔드 도구, 풀스택 앱, 브라우저 호환 연동을 만들 때 | | [Go](/ko/sdk/go) | 가벼운 백엔드 서비스, CLI, 워커, 동시성이 필요한 배치 프로세스를 만들 때 | | [Rust](/ko/sdk/rust) | 타입 안정성, 예측 가능한 성능, 네이티브 오디오 처리 파이프라인이 중요할 때 | | [C#/.NET](/ko/sdk/csharp) | .NET 서비스, Windows 도구, Unity 앱, Blazor 애플리케이션을 만들 때 | | [Java](/ko/sdk/java) | JVM 백엔드, Spring 서비스, Java 중심 엔터프라이즈 코드베이스에 연동할 때 | | [Kotlin](/ko/sdk/kotlin) | Kotlin 중심 JVM 서비스나 Android 애플리케이션을 만들 때 | | [C/C++](/ko/sdk/c) | 네이티브 연동, 임베디드 지원, FFI 바인딩, 최소 런타임 오버헤드가 필요할 때 | | [Swift](/ko/sdk/swift) | iOS, macOS, watchOS, tvOS, visionOS 애플리케이션을 만들 때 | | [Zig](/ko/sdk/zig) | C 의존성 없이 명시적인 메모리 제어가 필요한 저수준 네이티브 연동을 만들 때 | | [PHP](/ko/sdk/php) | Laravel, WordPress, PHP 서버 렌더링 백엔드에 타입캐스트를 연동할 때 | | [Dart/Flutter](/ko/sdk/dart) | Flutter 모바일, 데스크톱, 웹 애플리케이션을 만들 때 | | [Ruby](/ko/sdk/ruby) | Rails 앱, Ruby 백엔드 작업, 내부 자동화 스크립트를 만들 때 | ## Utility SDK | SDK | 이런 경우에 사용하세요 | | --- | --- | | [Autotag SDK](/ko/bestpractice/autotag) | 전화번호, 날짜, 시간, 금액 같은 구조화된 텍스트를 TTS가 더 자연스럽게 읽도록 전처리해야 할 때 | ## 무음 길이 조절 `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. 일반·스트리밍·타임스탬프 TTS는 `output.remove_silence_ms`, Compose는 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 전달합니다. 반환 타임스탬프는 처리 후 오디오를 기준으로 하며, 명시적인 `pause` 세그먼트는 유지됩니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. | SDK | 최소 지원 버전 | | --- | --- | | [python](/docs/ko/sdk/python) | 0.3.15 | | [javascript](/docs/ko/sdk/javascript) | 0.4.13 | | [go](/docs/ko/sdk/go) | 0.3.14 | | [rust](/docs/ko/sdk/rust) | 0.3.15 | | [csharp](/docs/ko/sdk/csharp) | 0.3.13 | | [java](/docs/ko/sdk/java) | 1.2.12 | | [kotlin](/docs/ko/sdk/kotlin) | 1.2.13 | | [c](/docs/ko/sdk/c) | 1.2.13 | | [swift](/docs/ko/sdk/swift) | 0.3.14 | | [zig](/docs/ko/sdk/zig) | 0.2.12 | | [php](/docs/ko/sdk/php) | 0.1.14 | | [dart](/docs/ko/sdk/dart) | 0.1.13 | | [ruby](/docs/ko/sdk/ruby) | 0.1.11 | --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Python 타입캐스트 Python SDK 타입캐스트 Python SDK 소스 코드 Python 3.10~3.14를 지원합니다. **Python 3.8, 3.9는 EOL로 인해 지원이 중단되었습니다.** 해당 버전들을 마지막으로 지원하는 SDK는 **typecast-python 0.3.15**입니다. Python을 3.10 이상(3.15 미만)으로 업그레이드한 뒤 최신 SDK를 설치하세요. 기존 환경을 임시 유지해야 한다면 `python -m pip install "typecast-python==0.3.15"`로 고정할 수 있지만, EOL 런타임의 보안 지원이 복구되는 것은 아닙니다. ## 설치 pip를 사용하여 타입캐스트 Python SDK를 설치하세요: ```bash pip install --upgrade typecast-python ``` 패키지는 `typecast-python`으로 설치되지만, `typecast`로 임포트합니다. 최신 등록 버전은 PyPI 기준 **0.4.0**입니다. **버전 0.4.0 이상**이 설치되어 있는지 확인하세요. `pip show typecast-python`으로 버전을 확인할 수 있습니다. 이전 버전이 있다면 `pip install --upgrade typecast-python`을 실행하여 업데이트하세요. ## 빠른 시작 텍스트를 음성으로 변환하는 간단한 예제입니다: ```python from typecast import Typecast from typecast.models import TTSRequest # 클라이언트 초기화 client = Typecast(api_key="YOUR_API_KEY") # 텍스트를 음성으로 변환 response = client.text_to_speech(TTSRequest( text="Hello there! I'm your friendly text-to-speech agent.", model="ssfm-v30", voice_id="tc_672c5f5ce59fac2a48faeaee" )) # 오디오 파일 저장 with open('output.wav', 'wb') as f: f.write(response.audio_data) print(f"Duration: {response.duration}s, Format: {response.format}") ``` ## 기능 타입캐스트 Python SDK는 텍스트 음성 변환을 위한 강력한 기능을 제공합니다: - **다중 음성 모델**: `ssfm-v30`(최신) 및 `ssfm-v21` AI 음성 모델 지원 - **다국어 지원**: 영어, 한국어, 스페인어, 일본어, 중국어 등 35+개 언어 지원 - **감정 조절**: 감정 프리셋(normal, happy, sad, angry, whisper, toneup, tonedown) 또는 스마트 문맥 인식 추론 - **오디오 사용자 정의**: 라우드니스(LUFS -70 to 0), 피치(-12 to +12 반음), 템포(0.5x to 2.0x), 형식(WAV/MP3) 제어 - **비동기 지원**: 고성능 애플리케이션을 위한 내장 비동기 클라이언트 - **보이스 탐색**: 모델, 성별, 나이, 사용 사례별 필터링이 가능한 V2 Voices API - **타임스탬프 TTS**: 자막, 가라오케, 립싱크를 위한 단어·문자 단위 정렬 데이터 - **스트리밍**: 저지연 재생을 위한 실시간 청크 오디오 전송 - **타입 힌트**: Pydantic 모델을 사용한 완전한 타입 주석 ## 보이스 추천 원하는 스타일은 알지만 정확한 `voice_id`를 모를 때 `recommend_voices`를 사용합니다. ```python recommendations = client.recommend_voices( "warm female voice for a product tutorial", count=3, ) for voice in recommendations: print(voice.voice_id, voice.voice_name, voice.score) ``` 추천 결과에는 `voice_id`, `voice_name`, `score`만 포함됩니다. 지원 모델, 감정, 성별, 연령대, 사용 사례 같은 상세 메타데이터가 필요하면 `voice_v2(voice_id)` 또는 `voices_v2()`로 추가 조회하세요. ## 설정 환경 변수를 사용하거나 클라이언트에 직접 전달하여 API 키를 구성할 수 있습니다: ```bash 환경 변수 export TYPECAST_API_KEY="your-api-key-here" ``` ```python 환경 변수에서 from typecast import Typecast # 환경 변수에서 client = Typecast() ``` ```python 직접 구성 from typecast import Typecast # 또는 직접 전달 client = Typecast(api_key="your-api-key-here") ``` 자체 프록시를 통해 요청하는 경우 `TYPECAST_API_HOST` 또는 `api_host`를 프록시 엔드포인트로 설정하고 `api_key`를 생략할 수 있습니다. API 키가 비어 있거나 없으면 SDK는 `X-API-KEY` 헤더를 보내지 않습니다. 기본 Typecast 호스트로 요청할 때는 API 키가 계속 필요합니다. ```python API 키 없는 프록시 from typecast import Typecast client = Typecast(api_host="https://your-proxy.example.com") ``` ## 고급 사용법 ### 감정 제어 (ssfm-v30) ssfm-v30은 두 가지 감정 제어 모드를 제공합니다: **프리셋** 및 **스마트**. AI가 문맥에서 감정을 추론하도록 합니다: ```python from typecast import Typecast from typecast.models import TTSRequest, SmartPrompt client = Typecast() response = client.text_to_speech(TTSRequest( text="Everything is going to be okay.", model="ssfm-v30", voice_id="tc_672c5f5ce59fac2a48faeaee", prompt=SmartPrompt( emotion_type="smart", previous_text="I just got the best news!", # 선택적 문맥 next_text="I can't wait to celebrate!" # 선택적 문맥 ) )) ``` 프리셋 값으로 감정을 명시적으로 설정합니다: ```python from typecast import Typecast from typecast.models import TTSRequest, PresetPrompt client = Typecast() response = client.text_to_speech(TTSRequest( text="I am so excited to show you these features!", model="ssfm-v30", voice_id="tc_672c5f5ce59fac2a48faeaee", prompt=PresetPrompt( emotion_type="preset", emotion_preset="happy", # normal, happy, sad, angry, whisper, toneup, tonedown emotion_intensity=1.5 # 범위: 0.0 ~ 2.0 ) )) ``` ### 음성 조절 라우드니스, 피치, 템포 및 출력 형식을 제어합니다: ```python from typecast import Typecast from typecast.models import TTSRequest, Output client = Typecast() response = client.text_to_speech(TTSRequest( text="Customized audio output!", model="ssfm-v30", voice_id="tc_672c5f5ce59fac2a48faeaee", output=Output( target_lufs=-14.0, # 범위: -70 ~ 0 (LUFS) audio_pitch=2, # 범위: -12 to +12 반음 audio_tempo=1.2, # 범위: 0.5x to 2.0x audio_format="mp3" # 옵션: wav, mp3 ), seed=42 # 부호 없는 정수 시드 (재현 가능한 결과) )) ``` ### 파일로 바로 생성하기 오디오 데이터를 직접 다루지 않고 파일까지 바로 저장하려면 `generate_to_file`을 사용하세요. 모델은 기본적으로 `ssfm-v30`을 사용하며, `.mp3` / `.wav` 확장자는 출력 포맷이 없을 때 포맷 추론에 사용됩니다. 사용할 보이스 ID는 [Voices](https://studio.typecast.ai/developers/api/voices) 페이지에서 확인할 수 있습니다. ```python client.generate_to_file( 'output.mp3', text='Hello from Typecast.', voice_id='tc_672c5f5ce59fac2a48faeaee' # voice_id는 https://studio.typecast.ai/developers/api/voices 에서 확인하세요. ) # Async client await async_client.generate_to_file( 'output.mp3', text='Hello from Typecast.', voice_id='tc_672c5f5ce59fac2a48faeaee' # voice_id는 https://studio.typecast.ai/developers/api/voices 에서 확인하세요. ) ``` ### 텍스트만으로 쉼 표현 한 voice로 읽는 문장 안에 쉼만 넣고 싶다면 텍스트에 pause markup을 직접 작성합니다. `<|5s|>`, `<|1s|>`, `<|0.3s|>`, `<|0.34413s|>`처럼 쓰며 값은 초 단위이고 반드시 `s`로 끝납니다. 별도 pause 함수를 호출하지 않아도 텍스트만 보고 쉼 위치를 확인할 수 있습니다. ```python response = ( client.compose_speech() .defaults(voice_id="tc_672c5f5ce59fac2a48faeaee", model="ssfm-v30") .say("안녕하세요<|5s|>반갑습니다<|1s|>오늘<|2s|>날씨는 어떤 것 같으세요?") .generate() ) ``` ### 다중 화자 합성 한 파일 안에서 서로 다른 voice나 구간별 pitch, tempo, prompt, seed 같은 옵션을 조합해야 할 때 사용합니다. composer는 세그먼트를 `POST /v1/text-to-speech/compose`로 보내며 WAV 또는 MP3를 직접 반환합니다. 무음 제거는 TTS 세그먼트에 명시적으로 설정하고, 명시적인 쉼은 유지됩니다. ```python from typecast import Typecast from typecast.models import Output client = Typecast(api_key="YOUR_API_KEY") response = ( client.compose_speech() .defaults(voice_id="tc_672c5f5ce59fac2a48faeaee", model="ssfm-v30") .say("Hello there") .pause(5) .say("Nice to meet you", voice_id="tc_60e5426de8b95f1d3000d7b5", output=Output(audio_pitch=2)) .say("Today") .pause(2) .say("How does the weather feel?") .generate() ) with open("conversation.wav", "wb") as f: f.write(response.audio_data) ``` ### 보이스 탐색 (V2 API) 향상된 메타데이터로 사용 가능한 보이스를 나열하고 필터링합니다: ```python from typecast import Typecast from typecast.models import VoicesV2Filter, TTSModel, GenderEnum, AgeEnum client = Typecast() # 모든 음성 가져오기 voices = client.voices_v2() # 기준으로 필터링 filtered = client.voices_v2(VoicesV2Filter( model=TTSModel.SSFM_V30, gender=GenderEnum.FEMALE, age=AgeEnum.YOUNG_ADULT )) # 음성 정보 표시 for voice in voices: print(f"ID: {voice.voice_id}, Name: {voice.voice_name}") print(f"Gender: {voice.gender}, Age: {voice.age}") print(f"Models: {', '.join(m.version.value for m in voice.models)}") print(f"Use cases: {voice.use_cases}") ``` ### 비동기 클라이언트 고성능 애플리케이션의 경우 비동기 클라이언트를 사용하세요: ```python import asyncio from typecast import AsyncTypecast from typecast.models import TTSRequest async def main(): async with AsyncTypecast() as client: response = await client.text_to_speech(TTSRequest( text="Hello from async!", model="ssfm-v30", voice_id="tc_672c5f5ce59fac2a48faeaee" )) with open('async_output.wav', 'wb') as f: f.write(response.audio_data) asyncio.run(main()) ``` ### 스트리밍 저지연 재생을 위한 실시간 오디오 청크 스트리밍: ```python # pip install requests sounddevice import sounddevice as sd from typecast import Typecast from typecast.models import TTSRequestStream, OutputStream client = Typecast() request = TTSRequestStream( text="이 텍스트를 실시간으로 오디오로 스트리밍합니다.", model="ssfm-v30", voice_id="tc_672c5f5ce59fac2a48faeaee", output=OutputStream(audio_format="wav") ) with sd.RawOutputStream(samplerate=32000, channels=1, dtype="int16") as player: buf, first = bytearray(), True for chunk in client.text_to_speech_stream(request): if first: chunk = chunk[44:] # 44바이트 WAV 헤더 건너뛰기 first = False buf.extend(chunk) n = len(buf) - (len(buf) % 2) # int16 정렬 if n: player.write(bytes(buf[:n])) del buf[:n] ``` **WAV 스트리밍 형식:** 32000 Hz, 16비트, 모노 PCM. 첫 번째 청크에 44바이트 WAV 헤더(size = `0xFFFFFFFF`)가 포함되며, 이후 청크는 원시 PCM 데이터만 포함합니다. MP3 형식: 320 kbps, 44100 Hz, 각 청크는 독립적으로 디코딩 가능합니다. ## 타임스탬프 TTS `text_to_speech_with_timestamps()`는 `POST /v1/text-to-speech/with-timestamps`를 래핑하여 오디오와 함께 단어·문자 단위 정렬 데이터를 반환합니다. 자막 생성, 가라오케 하이라이트, 립싱크 등에 활용할 수 있습니다. ### 기본 사용법 ```python from typecast import Typecast from typecast.models import TTSRequestWithTimestamps client = Typecast(api_key="YOUR_API_KEY") result = client.text_to_speech_with_timestamps(TTSRequestWithTimestamps( text="Hello. How are you?", model="ssfm-v30", voice_id="tc_60e5426de8b95f1d3000d7b5", )) # 오디오 저장 with open("output.wav", "wb") as f: f.write(result.audio_bytes()) print(f"Duration: {result.audio_duration}s") for word in result.words: print(f" [{word.start_time:.3f}s – {word.end_time:.3f}s] {word.text}") ``` ### Granularity(정렬 단위) `granularity="word"`(기본값) 또는 `granularity="char"`를 지정하여 정렬 단위를 설정합니다. ```python # 문자 단위 정렬 - 일본어/중국어에 필수 result = client.text_to_speech_with_timestamps(TTSRequestWithTimestamps( text="Hello. How are you?", model="ssfm-v30", voice_id="tc_60e5426de8b95f1d3000d7b5", granularity="char", )) ``` ### 자막 내보내기 SRT 및 WebVTT 형식의 자막을 출력합니다. 자막은 문장 종결 부호(`. ? ! 。 ? !`)를 기준으로 분할되며 큐당 7초/42자 상한을 적용합니다(BBC/Netflix 자막 가이드라인). ```python # SRT 자막 내보내기 with open("output.srt", "w", encoding="utf-8") as f: f.write(result.to_srt()) # WebVTT 자막 내보내기 with open("output.vtt", "w", encoding="utf-8") as f: f.write(result.to_vtt()) ``` **일본어/중국어:** 공백이 없는 언어(jpn, zho)는 단어 단위 세그먼트가 문장 전체로 나옵니다. 이러한 언어에서는 `granularity="char"`를 사용하세요. ## 지원 언어 **권장**: 타입 안전한 언어 선택을 위해 `LanguageCode` enum을 사용하세요. ISO 639-3 코드를 문자열로 전달할 수도 있습니다 (예: `"eng"`). SDK는 ISO 639-3 코드로 35+개 언어를 지원합니다: | 언어 | 코드 | 언어 | 코드 | 언어 | 코드 | |----------|------|----------|------|----------|------| | 영어 | `eng` | 일본어 | `jpn` | 우크라이나어 | `ukr` | | 한국어 | `kor` | 그리스어 | `ell` | 인도네시아어 | `ind` | | 스페인어 | `spa` | 타밀어 | `tam` | 덴마크어 | `dan` | | 독일어 | `deu` | 타갈로그어 | `tgl` | 스웨덴어 | `swe` | | 프랑스어 | `fra` | 핀란드어 | `fin` | 말레이어 | `msa` | | 이탈리아어 | `ita` | 중국어 | `zho` | 체코어 | `ces` | | 폴란드어 | `pol` | 슬로바키아어 | `slk` | 포르투갈어 | `por` | | 네덜란드어 | `nld` | 아랍어 | `ara` | 불가리아어 | `bul` | | 러시아어 | `rus` | 크로아티아어 | `hrv` | 루마니아어 | `ron` | | 벵골어 | `ben` | 힌디어 | `hin` | 헝가리어 | `hun` | | 민난어 | `nan` | 노르웨이어 | `nor` | 펀자브어 | `pan` | | 태국어 | `tha` | 터키어 | `tur` | 베트남어 | `vie` | | 광둥어 | `yue` | | | | | 타입 안전한 언어 선택을 위해 `LanguageCode` enum을 사용하세요: ```python from typecast.models import TTSRequest, LanguageCode response = client.text_to_speech(TTSRequest( text="Hello", model="ssfm-v30", voice_id="tc_672c5f5ce59fac2a48faeaee", language=LanguageCode.ENG )) ``` ## 오류 처리 SDK는 다양한 HTTP 상태 코드에 대한 특정 예외를 제공합니다: ```python from typecast import ( Typecast, TypecastError, BadRequestError, UnauthorizedError, PaymentRequiredError, NotFoundError, UnprocessableEntityError, RateLimitError, InternalServerError, ) try: response = client.text_to_speech(request) except UnauthorizedError: print("Invalid API key") except PaymentRequiredError: print("Insufficient credits") except RateLimitError: print("Rate limit exceeded - please try again later") except TypecastError as e: print(f"Error {e.status_code}: {e.message}") ``` | 예외 | 상태 코드 | 설명 | |-----------|-------------|-------------| | `BadRequestError` | 400 | 잘못된 요청 파라미터 | | `UnauthorizedError` | 401 | 잘못되거나 누락된 API 키 | | `PaymentRequiredError` | 402 | 크레딧 부족 | | `NotFoundError` | 404 | 리소스를 찾을 수 없음 | | `UnprocessableEntityError` | 422 | 유효성 검사 오류 | | `RateLimitError` | 429 | 요청 한도 초과 | | `InternalServerError` | 500 | 서버 오류 | ## 무음 길이 조절 이 기능은 **0.3.15 이상**에서 지원합니다. `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. 일반·스트리밍·타임스탬프 TTS는 `output.remove_silence_ms`, Compose는 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 전달합니다. 반환 타임스탬프는 처리 후 오디오를 기준으로 하며, 명시적인 `pause` 세그먼트는 유지됩니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. 아래 출력 설정을 해당 요청의 `output`에 전달하세요. 스트리밍은 스트리밍 전용 출력 타입을 사용합니다. ```python from typecast.models import Output, OutputStream output = Output(remove_silence_ms=300) stream_output = OutputStream(remove_silence_ms=300) ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Javascript/Typescript [타입캐스트 API](https://studio.typecast.ai/developers/api)를 위한 공식 Node.js 라이브러리입니다. AI 기반 음성을 사용하여 텍스트를 생동감 있는 음성으로 변환하세요. Javascript와 TypeScript 모두에서 작동합니다. 전체 TypeScript 타입이 포함되어 있습니다. ESM 및 CommonJS를 지원합니다. Node.js 18+ 및 최신 브라우저에서 작동합니다. Node.js 16/17 사용자는 `isomorphic-fetch` 폴리필을 설치해야 합니다. Typecast Javascript/Typescript SDK Typecast Javascript/Typescript SDK 소스 코드 ## 설치 ```bash npm install @neosapience/typecast-js@latest ``` ```bash pnpm add @neosapience/typecast-js@latest ``` ```bash yarn add @neosapience/typecast-js@latest ``` 최신 등록 버전은 npm 기준 **0.4.13**입니다. **버전 0.4.13 이상**이 설치되어 있는지 확인하세요. `npm list @neosapience/typecast-js`로 버전을 확인할 수 있습니다. 이전 버전이 있다면 `npm update @neosapience/typecast-js`를 실행하여 업데이트하세요. ## 빠른 시작 ```typescript import { TypecastClient } from '@neosapience/typecast-js'; import fs from 'fs'; async function main() { const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' }); const audio = await client.textToSpeech({ text: "Hello there! I'm your friendly text-to-speech agent.", model: "ssfm-v30", voice_id: "tc_672c5f5ce59fac2a48faeaee" }); await fs.promises.writeFile(`output.${audio.format}`, Buffer.from(audio.audioData)); console.log(`Audio saved! Duration: ${audio.duration}s, Format: ${audio.format}`); } main(); ``` ```javascript const { TypecastClient } = require('@neosapience/typecast-js'); const fs = require('fs'); async function main() { const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' }); const audio = await client.textToSpeech({ text: "Hello there! I'm your friendly text-to-speech agent.", model: "ssfm-v30", voice_id: "tc_672c5f5ce59fac2a48faeaee" }); await fs.promises.writeFile(`output.${audio.format}`, Buffer.from(audio.audioData)); console.log(`Audio saved! Duration: ${audio.duration}s, Format: ${audio.format}`); } main(); ``` ## 기능 타입캐스트 Javascript/TypeScript SDK는 텍스트 음성 변환을 위한 강력한 기능을 제공합니다: - **다중 음성 모델**: `ssfm-v30`(최신) 및 `ssfm-v21` AI 음성 모델 지원 - **다국어 지원**: 영어, 한국어, 스페인어, 일본어, 중국어 등 35+개 언어 지원 - **감정 조절**: 감정 프리셋(normal, happy, sad, angry, whisper, toneup, tonedown) 또는 스마트 문맥 인식 추론 - **오디오 사용자 정의**: 라우드니스(LUFS -70 to 0), 피치(-12 to +12 반음), 템포(0.5x to 2.0x), 형식(WAV/MP3) 제어 - **보이스 탐색**: 모델, 성별, 연령대, 사용 사례별 필터링이 가능한 V2 Voices API - **TypeScript 지원**: 전체 타입 정의 포함 - **스트리밍**: 저지연 재생을 위한 실시간 청크 오디오 전송 - **타임스탬프 TTS**: 자막, 가라오케, 립싱크를 위한 단어·문자 단위 정렬 데이터 - **의존성 없음**: 네이티브 fetch API 사용 (Node.js 18+ 및 브라우저에서 작동) ## 보이스 추천 원하는 스타일은 알지만 정확한 `voice_id`를 모를 때 `recommendVoices`를 사용합니다. ```typescript const recommendations = await client.recommendVoices( 'warm female voice for a product tutorial', 3 ); for (const voice of recommendations) { console.log(voice.voice_id, voice.voice_name, voice.score); } ``` 추천 결과에는 `voice_id`, `voice_name`, `score`만 포함됩니다. 지원 모델, 감정, 성별, 연령대, 사용 사례 같은 상세 메타데이터가 필요하면 `getVoiceV2(voiceId)` 또는 `getVoicesV2()`로 추가 조회하세요. ## 설정 환경 변수 또는 생성자를 통해 API 키를 설정하세요: ```typescript // 환경 변수 사용 // export TYPECAST_API_KEY="your-api-key-here" const client = new TypecastClient({ apiKey: process.env.TYPECAST_API_KEY! }); // 또는 직접 전달 const client = new TypecastClient({ apiKey: 'your-api-key-here' }); ``` 자체 프록시를 통해 요청하는 경우 `baseHost`를 프록시 엔드포인트로 설정하고 `apiKey`를 생략할 수 있습니다. API 키가 비어 있거나 없으면 SDK는 `X-API-KEY` 헤더를 보내지 않습니다. ```typescript API 키 없는 프록시 const client = new TypecastClient({ baseHost: 'https://your-proxy.example.com' }); ``` ## 고급 사용법 ### 감정 조절 (ssfm-v30) ssfm-v30은 두 가지 감정 제어 모드를 제공합니다: **프리셋** 및 **스마트**. AI가 문맥에서 감정을 추론하도록 합니다: ```typescript import { SmartPrompt } from '@neosapience/typecast-js'; const audio = await client.textToSpeech({ text: "Everything is going to be okay.", voice_id: "tc_672c5f5ce59fac2a48faeaee", model: "ssfm-v30", prompt: { emotion_type: "smart", previous_text: "I just got the best news!", // 선택적 문맥 next_text: "I can't wait to celebrate!" // 선택적 문맥 } as SmartPrompt }); ``` 프리셋 값으로 감정을 명시적으로 설정합니다: ```typescript import { PresetPrompt } from '@neosapience/typecast-js'; const audio = await client.textToSpeech({ text: "I am so excited to show you these features!", voice_id: "tc_672c5f5ce59fac2a48faeaee", model: "ssfm-v30", prompt: { emotion_type: "preset", emotion_preset: "happy", // normal, happy, sad, angry, whisper, toneup, tonedown emotion_intensity: 1.5 // 범위: 0.0 ~ 2.0 } as PresetPrompt }); ``` ### 음성 조절 라우드니스, 피치, 템포 및 출력 형식을 제어합니다: ```typescript const audio = await client.textToSpeech({ text: "Customized audio output!", voice_id: "tc_672c5f5ce59fac2a48faeaee", model: "ssfm-v30", output: { target_lufs: -14.0, // 범위: -70 ~ 0 (LUFS) audio_pitch: 2, // 범위: -12 to +12 반음 audio_tempo: 1.2, // 범위: 0.5x to 2.0x audio_format: "mp3" // 옵션: wav, mp3 }, seed: 42 // 부호 없는 정수 시드 (재현 가능한 결과) }); await fs.promises.writeFile(`output.${audio.format}`, Buffer.from(audio.audioData)); console.log(`Duration: ${audio.duration}s, Format: ${audio.format}`); ``` ### 파일로 바로 생성하기 오디오 데이터를 직접 다루지 않고 파일까지 바로 저장하려면 `generateToFile`을 사용하세요. 모델은 기본적으로 `ssfm-v30`을 사용하며, `.mp3` / `.wav` 확장자는 `output.audio_format`이 없을 때 출력 포맷 추론에 사용됩니다. 사용할 보이스 ID는 [Voices](https://studio.typecast.ai/developers/api/voices) 페이지에서 확인할 수 있습니다. ```typescript await client.generateToFile('output.mp3', { text: 'Hello from Typecast.', voice_id: 'tc_672c5f5ce59fac2a48faeaee' // voice_id는 https://studio.typecast.ai/developers/api/voices 에서 확인하세요. }); ``` ### 텍스트만으로 쉼 표현 한 voice로 읽는 문장 안에 쉼만 넣고 싶다면 텍스트에 pause markup을 직접 작성합니다. `<|5s|>`, `<|1s|>`, `<|0.3s|>`, `<|0.34413s|>`처럼 쓰며 값은 초 단위이고 반드시 `s`로 끝납니다. 별도 pause 함수를 호출하지 않아도 텍스트만 보고 쉼 위치를 확인할 수 있습니다. ```typescript const audio = await client .composeSpeech() .defaults({ voice_id: 'tc_672c5f5ce59fac2a48faeaee', model: 'ssfm-v30' }) .say('안녕하세요<|5s|>반갑습니다<|1s|>오늘<|2s|>날씨는 어떤 것 같으세요?') .generate(); ``` ### 다중 화자 합성 한 파일 안에서 서로 다른 voice나 구간별 pitch, tempo, prompt, seed 같은 옵션을 조합해야 할 때 사용합니다. composer는 세그먼트를 `POST /v1/text-to-speech/compose`로 보내며 WAV 또는 MP3를 직접 반환합니다. 무음 제거는 TTS 세그먼트에 명시적으로 설정하고, 명시적인 쉼은 유지됩니다. ```typescript import { TypecastClient } from '@neosapience/typecast-js'; import fs from 'fs/promises'; const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' }); const audio = await client .composeSpeech() .defaults({ voice_id: 'tc_672c5f5ce59fac2a48faeaee', model: 'ssfm-v30' }) .say('Hello there') .pause(5) .say('Nice to meet you', { voice_id: 'tc_60e5426de8b95f1d3000d7b5', output: { audio_pitch: 2 } }) .say('Today') .pause(2) .say('How does the weather feel?') .generate(); await fs.writeFile('conversation.wav', Buffer.from(audio.audioData)); ``` ### 보이스 탐색 (V2 API) 향상된 메타데이터로 사용 가능한 보이스를 나열하고 필터링합니다: ```typescript // 모든 음성 가져오기 const voices = await client.getVoicesV2(); // 기준으로 필터링 const filtered = await client.getVoicesV2({ model: 'ssfm-v30', gender: 'female', age: 'young_adult' }); // 음성 정보 표시 voices.forEach(voice => { console.log(`ID: ${voice.voice_id}, Name: ${voice.voice_name}`); console.log(`Gender: ${voice.gender}, Age: ${voice.age}`); console.log(`Models: ${voice.models.map(m => m.version).join(', ')}`); console.log(`Use cases: ${voice.use_cases?.join(', ')}`); }); ``` ### 다국어 콘텐츠 SDK는 자동 언어 감지와 함께 35+개 언어를 지원합니다: ```typescript // 언어 자동 감지 (권장) const audio = await client.textToSpeech({ text: "こんにちは。お元気ですか。", voice_id: "tc_672c5f5ce59fac2a48faeaee", model: "ssfm-v30" }); // 또는 명시적으로 언어 지정 const koreanAudio = await client.textToSpeech({ text: "안녕하세요. 반갑습니다.", voice_id: "tc_672c5f5ce59fac2a48faeaee", model: "ssfm-v30", language: "kor" // ISO 639-3 언어 코드 }); await fs.promises.writeFile(`output.${audio.format}`, Buffer.from(audio.audioData)); ``` ### 스트리밍 저지연 재생을 위한 실시간 오디오 청크 스트리밍: ```javascript // Node 18+ (내장 fetch). 스트림을 ffplay로 파이핑하여 실시간 재생. // 사전 설치: ffmpeg (brew/choco/apt install ffmpeg) import { spawn } from "node:child_process"; import { TypecastClient } from '@neosapience/typecast-js'; const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' }); const ffplay = spawn( "ffplay", ["-autoexit", "-nodisp", "-loglevel", "error", "-i", "pipe:0"], { stdio: ["pipe", "ignore", "ignore"] }, ); const stream = await client.textToSpeechStream({ text: "이 텍스트를 실시간으로 오디오로 스트리밍합니다.", model: "ssfm-v30", voice_id: "tc_672c5f5ce59fac2a48faeaee", output: { audio_format: "wav" } }); // ReadableStream - 청크가 도착하는 즉시 읽습니다 const reader = stream.getReader(); while (true) { const { value, done } = await reader.read(); if (done) break; ffplay.stdin.write(value); } ffplay.stdin.end(); await new Promise((resolve) => ffplay.on("close", resolve)); ``` **WAV 스트리밍 형식:** 32000 Hz, 16비트, 모노 PCM. 첫 번째 청크에 44바이트 WAV 헤더(size = `0xFFFFFFFF`)가 포함되며, 이후 청크는 원시 PCM 데이터만 포함합니다. MP3 형식: 320 kbps, 44100 Hz, 각 청크는 독립적으로 디코딩 가능합니다. ## 타임스탬프 TTS `textToSpeechWithTimestamps()`는 `POST /v1/text-to-speech/with-timestamps`를 래핑하여 오디오와 함께 단어·문자 단위 정렬 데이터를 반환합니다. 자막 생성, 가라오케 하이라이트, 립싱크 등에 활용할 수 있습니다. ### 기본 사용법 ```typescript import { TypecastClient } from '@neosapience/typecast-js'; import fs from 'fs'; const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' }); const result = await client.textToSpeechWithTimestamps({ text: "Hello. How are you?", model: "ssfm-v30", voice_id: "tc_60e5426de8b95f1d3000d7b5", }); await fs.promises.writeFile("output.wav", result.audioBytes()); console.log(`Duration: ${result.audio_duration}s`); result.words.forEach(w => { console.log(` [${w.start_time.toFixed(3)}s – ${w.end_time.toFixed(3)}s] ${w.text}`); }); ``` ### 자막 내보내기 ```typescript await fs.promises.writeFile("output.srt", result.toSrt(), "utf-8"); await fs.promises.writeFile("output.vtt", result.toVtt(), "utf-8"); ``` **일본어/중국어:** `granularity: "char"`를 사용하세요. 공백이 없는 언어에서는 단어 단위 세그먼트가 문장 전체로 나옵니다. ## 지원 언어 SDK는 자동 언어 감지와 함께 35+개 언어를 지원합니다: | 코드 | 언어 | 코드 | 언어 | 코드 | 언어 | |------|----------|------|----------|------|----------| | `eng` | 영어 | `jpn` | 일본어 | `ukr` | 우크라이나어 | | `kor` | 한국어 | `ell` | 그리스어 | `ind` | 인도네시아어 | | `spa` | 스페인어 | `tam` | 타밀어 | `dan` | 덴마크어 | | `deu` | 독일어 | `tgl` | 타갈로그어 | `swe` | 스웨덴어 | | `fra` | 프랑스어 | `fin` | 핀란드어 | `msa` | 말레이어 | | `ita` | 이탈리아어 | `zho` | 중국어 | `ces` | 체코어 | | `pol` | 폴란드어 | `slk` | 슬로바키아어 | `por` | 포르투갈어 | | `nld` | 네덜란드어 | `ara` | 아랍어 | `bul` | 불가리아어 | | `rus` | 러시아어 | `hrv` | 크로아티아어 | `ron` | 루마니아어 | | `ben` | 벵골어 | `hin` | 힌디어 | `hun` | 헝가리어 | | `nan` | 민난어 | `nor` | 노르웨이어 | `pan` | 펀자브어 | | `tha` | 태국어 | `tur` | 터키어 | `vie` | 베트남어 | | `yue` | 광둥어 | | | | | 지정하지 않으면 입력 텍스트에서 언어가 자동으로 감지됩니다. ## 오류 처리 SDK는 API 오류 처리를 위한 `TypecastAPIError`를 제공합니다: ```typescript import { TypecastClient, TypecastAPIError } from '@neosapience/typecast-js'; try { const audio = await client.textToSpeech({ text: "Hello world", voice_id: "tc_672c5f5ce59fac2a48faeaee", model: "ssfm-v30" }); } catch (error) { if (error instanceof TypecastAPIError) { // TypecastAPIError는 statusCode, message, response를 노출합니다 switch (error.statusCode) { case 401: console.error('Invalid API key'); break; case 402: console.error('Insufficient credits'); break; case 422: console.error('Validation error:', error.response); break; case 429: console.error('Rate limit exceeded - please try again later'); break; default: console.error(`API error (${error.statusCode}):`, error.message); } } else { console.error('Unexpected error:', error); } } ``` ## TypeScript 지원 이 SDK는 TypeScript로 작성되었으며 전체 타입 정의를 제공합니다: ```typescript import type { TTSRequest, TTSResponse, TTSModel, LanguageCode, Prompt, PresetPrompt, SmartPrompt, Output, VoiceV2Response, VoicesV2Filter } from '@neosapience/typecast-js'; ``` ## 무음 길이 조절 이 기능은 **0.4.13 이상**에서 지원합니다. `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. 일반·스트리밍·타임스탬프 TTS는 `output.remove_silence_ms`, Compose는 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 전달합니다. 반환 타임스탬프는 처리 후 오디오를 기준으로 하며, 명시적인 `pause` 세그먼트는 유지됩니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. 아래 출력 설정을 해당 요청의 `output`에 전달하세요. 스트리밍은 스트리밍 전용 출력 타입을 사용합니다. ```typescript const output = { remove_silence_ms: 300 }; ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Go [타입캐스트 API](https://typecast.ai)를 위한 공식 Go 라이브러리입니다. AI 기반 음성을 사용하여 텍스트를 생동감 있는 음성으로 변환하세요. Go 1.21 이상 버전과 호환됩니다. 외부 의존성 없이 Go 표준 라이브러리만 사용합니다. Typecast Go SDK Typecast Go SDK 소스 코드 ## 설치 ```bash go get github.com/neosapience/typecast-sdk/typecast-go ``` 최신 등록 버전은 Go modules 기준 **typecast-go/v0.3.14**입니다. **Go 1.21 이상**이 설치되어 있는지 확인하세요. `go version` 명령으로 버전을 확인할 수 있습니다. ## 빠른 시작 ```go package main import ( "context" "os" typecast "github.com/neosapience/typecast-sdk/typecast-go" ) func main() { // 클라이언트 초기화 client := typecast.NewClient(&typecast.ClientConfig{ APIKey: "YOUR_API_KEY", }) ctx := context.Background() // 텍스트를 음성으로 변환 response, err := client.TextToSpeech(ctx, &typecast.TTSRequest{ VoiceID: "tc_672c5f5ce59fac2a48faeaee", Text: "안녕하세요! 저는 텍스트 음성 변환 에이전트입니다.", Model: typecast.ModelSSFMV30, }) if err != nil { panic(err) } // 오디오 파일 저장 os.WriteFile("output.wav", response.AudioData, 0644) println("Audio saved! Format:", string(response.Format)) } ``` ## 기능 Typecast Go SDK는 텍스트 음성 변환을 위한 강력한 기능을 제공합니다: - **다중 음성 모델**: `ssfm-v30`(최신) 및 `ssfm-v21` AI 음성 모델 지원 - **다국어 지원**: 영어, 한국어, 스페인어, 일본어, 중국어 등 35+개 언어 지원 - **감정 제어**: 이모션 프리셋(normal, happy, sad, angry, whisper, toneup, tonedown) 또는 스마트 문맥 인식 추론 - **오디오 커스터마이징**: 라우드니스(LUFS -70 to 0), 피치(-12 to +12 반음), 템포(0.5x to 2.0x), 형식(WAV/MP3) 제어 - **음성 검색**: 모델, 성별, 나이, 사용 사례별 필터링이 가능한 V2 Voices API - **Context 지원**: 취소 및 타임아웃을 위한 `context.Context` 완전 지원 - **타임스탬프 TTS**: 자막, 가라오케, 립싱크를 위한 단어·문자 단위 정렬 데이터 - **스트리밍**: 저지연 재생을 위한 실시간 청크 오디오 전송 - **의존성 없음**: Go 표준 라이브러리만 사용 ## 보이스 추천 원하는 스타일은 알지만 정확한 `voice_id`를 모를 때 `RecommendVoices`를 사용합니다. ```go voices, err := client.RecommendVoices( context.Background(), "warm female voice for a product tutorial", 3, ) if err != nil { panic(err) } for _, voice := range voices { fmt.Println(voice.VoiceID, voice.VoiceName, voice.Score) } ``` 추천 결과에는 `VoiceID`, `VoiceName`, `Score`만 포함됩니다. 지원 모델, 감정, 성별, 연령대, 사용 사례 같은 상세 메타데이터가 필요하면 `GetVoiceV2` 또는 `GetVoicesV2`로 추가 조회하세요. ## 설정 환경 변수 또는 직접 전달로 API 키를 설정하세요: ```go import typecast "github.com/neosapience/typecast-sdk/typecast-go" // 환경 변수 사용 (권장) // export TYPECAST_API_KEY="your-api-key-here" client := typecast.NewClient(nil) // 또는 직접 전달 client := typecast.NewClient(&typecast.ClientConfig{ APIKey: "your-api-key-here", }) // 사용자 정의 설정 client := typecast.NewClient(&typecast.ClientConfig{ APIKey: "your-api-key-here", BaseURL: "https://api.typecast.ai", // 선택사항 Timeout: 60 * time.Second, // 선택사항 }) ``` 자체 프록시를 통해 요청하는 경우 `BaseURL` 또는 `TYPECAST_API_HOST`를 프록시 엔드포인트로 설정하고 `APIKey`를 생략할 수 있습니다. API 키가 비어 있거나 없으면 SDK는 `X-API-KEY` 헤더를 보내지 않습니다. 기본 Typecast 호스트로 요청할 때는 API 키가 계속 필요합니다. ```go API 키 없는 프록시 client := typecast.NewClient(&typecast.ClientConfig{ BaseURL: "https://your-proxy.example.com", }) ``` ### 환경 변수 | 변수 | 설명 | |------|------| | `TYPECAST_API_KEY` | 타입캐스트 API 키 | | `TYPECAST_API_HOST` | 사용자 정의 API 기본 URL (선택사항) | ## 고급 사용법 ### 감정 제어 (ssfm-v30) ssfm-v30은 두 가지 감정 제어 모드를 제공합니다: **프리셋**과 **스마트**. AI가 문맥에서 감정을 추론하도록 합니다: ```go response, err := client.TextToSpeech(ctx, &typecast.TTSRequest{ VoiceID: "tc_672c5f5ce59fac2a48faeaee", Text: "모든 것이 잘 될 거예요.", Model: typecast.ModelSSFMV30, Prompt: &typecast.SmartPrompt{ EmotionType: "smart", PreviousText: "방금 최고의 소식을 들었어요!", // 선택적 문맥 NextText: "축하하고 싶어요!", // 선택적 문맥 }, }) ``` 프리셋 값으로 감정을 명시적으로 설정합니다: ```go intensity := 1.5 response, err := client.TextToSpeech(ctx, &typecast.TTSRequest{ VoiceID: "tc_672c5f5ce59fac2a48faeaee", Text: "이 기능들을 보여드리게 되어 정말 신나요!", Model: typecast.ModelSSFMV30, Prompt: &typecast.PresetPrompt{ EmotionType: "preset", EmotionPreset: typecast.EmotionHappy, // normal, happy, sad, angry, whisper, toneup, tonedown EmotionIntensity: &intensity, // 범위: 0.0 ~ 2.0 }, }) ``` ### 오디오 커스터마이징 라우드니스, 피치, 템포, 출력 형식을 제어합니다: ```go lufs := -14.0 pitch := 2 tempo := 1.2 seed := uint32(42) response, err := client.TextToSpeech(ctx, &typecast.TTSRequest{ VoiceID: "tc_672c5f5ce59fac2a48faeaee", Text: "커스터마이즈된 오디오 출력!", Model: typecast.ModelSSFMV30, Output: &typecast.Output{ TargetLUFS: &lufs, // 범위: -70 ~ 0 (LUFS) AudioPitch: &pitch, // 범위: -12 to +12 반음 AudioTempo: &tempo, // 범위: 0.5x to 2.0x AudioFormat: typecast.AudioFormatMP3, // 옵션: WAV, MP3 }, Seed: &seed, // 재현 가능한 결과를 위해 }) os.WriteFile("output.mp3", response.AudioData, 0644) fmt.Printf("Format: %s\n", response.Format) ``` ### 파일로 바로 생성하기 `GenerateToFile`은 음성 합성과 파일 저장을 한 번에 처리합니다. `model`은 기본값으로 `ssfm-v30`을 사용하고, `.mp3` 또는 `.wav` 확장자로 출력 형식을 결정합니다. ```go _, err := client.GenerateToFile(ctx, "output.mp3", typecast.GenerateToFileRequest{ Text: "안녕하세요, 타입캐스트입니다.", VoiceID: "tc_672c5f5ce59fac2a48faeaee", // voice_id는 https://studio.typecast.ai/developers/api/voices 에서 확인하세요. }) if err != nil { panic(err) } ``` ### 텍스트만으로 쉼 표현 한 voice로 읽는 문장 안에 쉼만 넣고 싶다면 텍스트에 pause markup을 직접 작성합니다. `<|5s|>`, `<|1s|>`, `<|0.3s|>`, `<|0.34413s|>`처럼 쓰며 값은 초 단위이고 반드시 `s`로 끝납니다. 별도 pause 함수를 호출하지 않아도 텍스트만 보고 쉼 위치를 확인할 수 있습니다. ```go audio, err := client.ComposeSpeech(). Defaults(typecast.ComposerSettings{VoiceID: "tc_672c5f5ce59fac2a48faeaee", Model: typecast.TTSModelSSFMV30}). Say("안녕하세요<|5s|>반갑습니다<|1s|>오늘<|2s|>날씨는 어떤 것 같으세요?"). Generate(ctx) ``` ### 다중 화자 합성 한 파일 안에서 서로 다른 voice나 구간별 pitch, tempo, prompt, seed 같은 옵션을 조합해야 할 때 사용합니다. composer는 세그먼트를 `POST /v1/text-to-speech/compose`로 보내며 WAV 또는 MP3를 직접 반환합니다. 무음 제거는 TTS 세그먼트에 명시적으로 설정하고, 명시적인 쉼은 유지됩니다. ```go package main import ( "context" "os" typecast "github.com/neosapience/typecast-sdk/typecast-go" ) func main() { client := typecast.NewClient("YOUR_API_KEY") response, err := client.ComposeSpeech(). Defaults(typecast.ComposerSettings{VoiceID: "tc_672c5f5ce59fac2a48faeaee", Model: typecast.ModelSSFMV30}). Say("Hello there"). Pause(5). SayWith("Nice to meet you", typecast.ComposerSettings{VoiceID: "tc_60e5426de8b95f1d3000d7b5", Output: &typecast.Output{AudioPitch: 2}}). Say("Today"). Pause(2). Say("How does the weather feel?"). Generate(context.Background()) if err != nil { panic(err) } _ = os.WriteFile("conversation.wav", response.AudioData, 0644) } ``` ### 음성 검색 (V2 API) 향상된 메타데이터로 사용 가능한 음성을 나열하고 필터링합니다: ```go // 모든 음성 가져오기 voices, err := client.GetVoicesV2(ctx, nil) // 조건별 필터링 voices, err := client.GetVoicesV2(ctx, &typecast.VoicesV2Filter{ Model: typecast.ModelSSFMV30, Gender: typecast.GenderFemale, Age: typecast.AgeYoungAdult, }) // 음성 정보 표시 for _, voice := range voices { fmt.Printf("ID: %s, Name: %s\n", voice.VoiceID, voice.VoiceName) if voice.Gender != nil { fmt.Printf("Gender: %s\n", *voice.Gender) } if voice.Age != nil { fmt.Printf("Age: %s\n", *voice.Age) } for _, model := range voice.Models { fmt.Printf("Model: %s, Emotions: %v\n", model.Version, model.Emotions) } } // 특정 음성 상세 정보 가져오기 voice, err := client.GetVoiceV2(ctx, "tc_672c5f5ce59fac2a48faeaee") ``` ### 다국어 콘텐츠 SDK는 자동 언어 감지를 통해 35+개 언어를 지원합니다: ```go // 언어 자동 감지 (권장) response, err := client.TextToSpeech(ctx, &typecast.TTSRequest{ VoiceID: "tc_672c5f5ce59fac2a48faeaee", Text: "こんにちは。お元気ですか。", Model: typecast.ModelSSFMV30, }) // 또는 언어 명시적으로 지정 response, err := client.TextToSpeech(ctx, &typecast.TTSRequest{ VoiceID: "tc_672c5f5ce59fac2a48faeaee", Text: "안녕하세요. 반갑습니다.", Model: typecast.ModelSSFMV30, Language: "kor", // ISO 639-3 언어 코드 }) ``` ### 스트리밍 저지연 재생을 위한 실시간 오디오 청크 스트리밍: ```go // 원시 PCM 추출 (44바이트 WAV 헤더 건너뛰기) reader, _ := client.TextToSpeechStream(context.Background(), request) defer reader.Close() buf := make([]byte, 4096) first := true for { n, err := reader.Read(buf) if n > 0 { data := buf[:n] if first { data = data[44:] // WAV 헤더 건너뛰기 first = false } // data는 32000 Hz 16비트 모노 원시 PCM // 오디오 출력으로 전달 (예: oto, portaudio) _ = data } if err != nil { break } } ``` **WAV 스트리밍 형식:** 32000 Hz, 16비트, 모노 PCM. 첫 번째 청크에 44바이트 WAV 헤더(size = `0xFFFFFFFF`)가 포함되며, 이후 청크는 원시 PCM 데이터만 포함합니다. MP3 형식: 320 kbps, 44100 Hz, 각 청크는 독립적으로 디코딩 가능합니다. ## 타임스탬프 TTS `TextToSpeechWithTimestamps()`는 `POST /v1/text-to-speech/with-timestamps`를 래핑하며, 오디오와 함께 단어·문자 단위 정렬 데이터를 반환합니다. 가라오케 하이라이트, 자막 생성, 립싱크 애플리케이션에 활용할 수 있습니다. ### 기본 사용법 ```go package main import ( "context" "fmt" "os" typecast "github.com/neosapience/typecast-sdk/typecast-go" ) func main() { client := typecast.NewClient(&typecast.ClientConfig{APIKey: "YOUR_API_KEY"}) ctx := context.Background() result, err := client.TextToSpeechWithTimestamps(ctx, &typecast.TTSRequestWithTimestamps{ VoiceID: "tc_60e5426de8b95f1d3000d7b5", Text: "Hello. How are you?", Model: typecast.ModelSSFMV30, }) if err != nil { panic(err) } os.WriteFile("output.wav", result.AudioBytes(), 0644) fmt.Printf("재생 시간: %.3f초\n", result.AudioDuration) for _, w := range result.Words { fmt.Printf(" [%.3fs – %.3fs] %s\n", w.StartTime, w.EndTime, w.Text) } } ``` ### 정밀도(Granularity) 설정 `Granularity: typecast.GranularityWord`(기본값) 또는 `Granularity: typecast.GranularityChar`를 전달해 정렬 단위를 제어합니다. ```go // 문자 단위 정렬 - 일본어·중국어에 필수 result, err := client.TextToSpeechWithTimestamps(ctx, &typecast.TTSRequestWithTimestamps{ VoiceID: "tc_60e5426de8b95f1d3000d7b5", Text: "Hello. How are you?", Model: typecast.ModelSSFMV30, Granularity: typecast.GranularityChar, }) ``` ### 자막 내보내기 ```go srt, _ := result.ToSrt() os.WriteFile("output.srt", []byte(srt), 0644) vtt, _ := result.ToVtt() os.WriteFile("output.vtt", []byte(vtt), 0644) ``` **일본어·중국어:** 공백 구분자가 없는 언어(jpn, zho)는 단어 단위 세그먼트가 의미를 갖지 않습니다. 해당 언어에는 `GranularityChar`를 사용해 문자 단위 정렬 데이터를 얻으세요. ## 지원 언어 SDK는 자동 언어 감지를 통해 35+개 언어를 지원합니다: | 코드 | 언어 | 코드 | 언어 | 코드 | 언어 | |------|------|------|------|------|------| | `eng` | 영어 | `jpn` | 일본어 | `ukr` | 우크라이나어 | | `kor` | 한국어 | `ell` | 그리스어 | `ind` | 인도네시아어 | | `spa` | 스페인어 | `tam` | 타밀어 | `dan` | 덴마크어 | | `deu` | 독일어 | `tgl` | 타갈로그어 | `swe` | 스웨덴어 | | `fra` | 프랑스어 | `fin` | 핀란드어 | `msa` | 말레이어 | | `ita` | 이탈리아어 | `zho` | 중국어 | `ces` | 체코어 | | `pol` | 폴란드어 | `slk` | 슬로바키아어 | `por` | 포르투갈어 | | `nld` | 네덜란드어 | `ara` | 아랍어 | `bul` | 불가리아어 | | `rus` | 러시아어 | `hrv` | 크로아티아어 | `ron` | 루마니아어 | | `ben` | 벵골어 | `hin` | 힌디어 | `hun` | 헝가리어 | | `nan` | 민난어 | `nor` | 노르웨이어 | `pan` | 펀자브어 | | `tha` | 태국어 | `tur` | 터키어 | `vie` | 베트남어 | | `yue` | 광동어 | | | | | 지정하지 않으면 입력 텍스트에서 자동으로 언어가 감지됩니다. ## 오류 처리 SDK는 특정 오류를 처리하기 위한 헬퍼 메서드가 있는 `APIError` 타입을 제공합니다: ```go import typecast "github.com/neosapience/typecast-sdk/typecast-go" response, err := client.TextToSpeech(ctx, request) if err != nil { if apiErr, ok := err.(*typecast.APIError); ok { fmt.Printf("Error %d: %s\n", apiErr.StatusCode, apiErr.Message) // 특정 오류 처리 switch { case apiErr.IsUnauthorized(): // 401: 잘못된 API 키 case apiErr.IsForbidden(): // 403: 접근 거부 case apiErr.IsPaymentRequired(): // 402: 크레딧 부족 case apiErr.IsNotFound(): // 404: 리소스를 찾을 수 없음 case apiErr.IsValidationError(): // 422: 유효성 검사 오류 case apiErr.IsRateLimited(): // 429: 요청 제한 초과 case apiErr.IsServerError(): // 5xx: 서버 오류 case apiErr.IsBadRequest(): // 400: 잘못된 요청 } } } ``` ### 오류 유형 | 메서드 | 상태 코드 | 설명 | |--------|-----------|------| | `IsBadRequest()` | 400 | 잘못된 요청 매개변수 | | `IsUnauthorized()` | 401 | 잘못되거나 누락된 API 키 | | `IsPaymentRequired()` | 402 | 크레딧 부족 | | `IsForbidden()` | 403 | 접근 거부 | | `IsNotFound()` | 404 | 리소스를 찾을 수 없음 | | `IsValidationError()` | 422 | 유효성 검사 오류 | | `IsRateLimited()` | 429 | 요청 제한 초과 | | `IsServerError()` | 5xx | 서버 오류 | ## Context와 타임아웃 SDK는 취소 및 타임아웃을 위한 Go의 `context.Context`를 완전히 지원합니다: ```go // 타임아웃 사용 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() response, err := client.TextToSpeech(ctx, request) if err != nil { if ctx.Err() == context.DeadlineExceeded { fmt.Println("Request timed out") } } // 취소 사용 ctx, cancel := context.WithCancel(context.Background()) go func() { time.Sleep(5 * time.Second) cancel() // 5초 후 취소 }() response, err := client.TextToSpeech(ctx, request) ``` ## API 레퍼런스 ### 클라이언트 메서드 | 메서드 | 설명 | |--------|------| | `TextToSpeech(ctx, request)` | 텍스트를 음성 오디오로 변환 | | `GenerateToFile(ctx, path, request)` | 음성을 생성하고 로컬 파일로 바로 저장 | | `GetVoicesV2(ctx, filter)` | 필터링으로 사용 가능한 음성 가져오기 | | `GetVoiceV2(ctx, voiceID)` | ID로 특정 음성 가져오기 | | `GetVoices(ctx, model)` | 음성 가져오기 (V1 API, 권장하지 않음) | | `GetVoice(ctx, voiceID, model)` | 음성 가져오기 (V1 API, 권장하지 않음) | ### TTSRequest 필드 | 필드 | 타입 | 필수 | 설명 | |------|------|------|------| | `VoiceID` | `string` | ✓ | 음성 ID (형식: `tc_*` 또는 `uc_*`) | | `Text` | `string` | ✓ | 합성할 텍스트 (최대 2000자) | | `Model` | `TTSModel` | ✓ | TTS 모델 (`ModelSSFMV21` 또는 `ModelSSFMV30`) | | `Language` | `string` | | ISO 639-3 코드 (생략 시 자동 감지) | | `Prompt` | `*Prompt` / `*PresetPrompt` / `*SmartPrompt` | | 감정 설정 | | `Output` | `*Output` | | 오디오 출력 설정 | | `Seed` | `*uint32` | | 재현성을 위한 부호 없는 정수 시드 (≥ 0) | ### TTSResponse 필드 | 필드 | 타입 | 설명 | |------|------|------| | `AudioData` | `[]byte` | 생성된 오디오 데이터 | | `Duration` | `float64` | 오디오 길이 (초) | | `Format` | `AudioFormat` | 오디오 형식 (`wav` 또는 `mp3`) | ### 상수 #### 모델 | 상수 | 값 | 설명 | |------|-----|------| | `ModelSSFMV30` | `ssfm-v30` | 향상된 운율의 최신 모델 | | `ModelSSFMV21` | `ssfm-v21` | 안정적인 프로덕션 모델 | #### 감정 프리셋 | 상수 | ssfm-v21 | ssfm-v30 | |------|----------|----------| | `EmotionNormal` | ✓ | ✓ | | `EmotionHappy` | ✓ | ✓ | | `EmotionSad` | ✓ | ✓ | | `EmotionAngry` | ✓ | ✓ | | `EmotionWhisper` | ✗ | ✓ | | `EmotionToneUp` | ✗ | ✓ | | `EmotionToneDown` | ✗ | ✓ | #### 오디오 형식 | 상수 | 값 | 설명 | |------|-----|------| | `AudioFormatWAV` | `wav` | 비압축 PCM 오디오 | | `AudioFormatMP3` | `mp3` | 압축된 MP3 오디오 | ## 무음 길이 조절 이 기능은 **0.3.14 이상**에서 지원합니다. `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. 일반·스트리밍·타임스탬프 TTS는 `output.remove_silence_ms`, Compose는 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 전달합니다. 반환 타임스탬프는 처리 후 오디오를 기준으로 하며, 명시적인 `pause` 세그먼트는 유지됩니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. 아래 출력 설정을 해당 요청의 `output`에 전달하세요. 스트리밍은 스트리밍 전용 출력 타입을 사용합니다. ```go remainingSilence := 300 output := &typecast.Output{RemoveSilenceMS: &remainingSilence} streamOutput := &typecast.OutputStream{RemoveSilenceMS: &remainingSilence} ``` `RemoveSilenceMS`는 포인터입니다. `nil`과 `0`을 구분하려면 위처럼 정수의 주소를 전달하세요. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Rust [타입캐스트 API](https://typecast.ai)를 위한 공식 Rust 라이브러리입니다. AI 기반 음성을 사용하여 텍스트를 생동감 있는 음성으로 변환하세요. Tokio 런타임을 사용한 async/await 지원으로 구축되었습니다. Cargo 패키지 관리자와 함께 작동합니다. Typecast Rust SDK Typecast Rust SDK 소스 코드 ## 설치 `Cargo.toml`에 다음을 추가하세요: ```toml [dependencies] typecast-rust = "0.3.15" tokio = { version = "1", features = ["full"] } ``` 또는 Cargo를 사용하여 의존성을 추가하세요: ```bash cargo add typecast-rust tokio --features tokio/full ``` 최신 등록 버전은 crates.io 기준 **0.3.15**입니다. **버전 0.3.15 이상**이 설치되어 있는지 확인하세요. 업데이트가 필요하면 `Cargo.toml`을 확인하세요. ## 빠른 시작 ```rust use typecast_rust::{TypecastClient, TTSRequest, TTSModel}; use std::fs; #[tokio::main] async fn main() -> Result<(), Box> { // 클라이언트 초기화 (환경변수에서 TYPECAST_API_KEY 읽기) let client = TypecastClient::from_env()?; // 텍스트를 음성으로 변환 let request = TTSRequest::new( "tc_672c5f5ce59fac2a48faeaee", "안녕하세요! 저는 텍스트 음성 변환 에이전트입니다.", TTSModel::SsfmV30, ); let response = client.text_to_speech(&request).await?; // 오디오 파일 저장 fs::write("output.wav", &response.audio_data)?; println!( "Audio saved! Duration: {:.2}s, Format: {:?}", response.duration, response.format ); Ok(()) } ``` ## 기능 Typecast Rust SDK는 텍스트 음성 변환을 위한 강력한 기능을 제공합니다: - **다중 음성 모델**: `ssfm-v30`(최신) 및 `ssfm-v21` AI 음성 모델 지원 - **다국어 지원**: 영어, 한국어, 스페인어, 일본어, 중국어 등 35+개 언어 지원 - **감정 제어**: 이모션 프리셋(normal, happy, sad, angry, whisper, toneup, tonedown) 또는 스마트 문맥 인식 추론 - **오디오 사용자 정의**: 라우드니스 LUFS(-70 to 0), 피치(-12 to +12 반음), 템포(0.5x to 2.0x), 형식(WAV/MP3) 제어 - **음성 탐색**: 모델, 성별, 나이, 사용 사례별 필터링이 가능한 V2 Voices API - **빌더 패턴**: 쉬운 요청 구성을 위한 메서드 체이닝 Fluent API - **Async/Await**: 효율적인 비동기 작업을 위해 Tokio 기반으로 구축 - **타임스탬프 TTS**: 자막, 가라오케, 립싱크를 위한 단어·문자 단위 정렬 데이터 - **스트리밍**: 저지연 재생을 위한 실시간 청크 오디오 전송 - **포괄적인 오류 처리**: 패턴 매칭을 지원하는 타입화된 에러 enum ## 보이스 추천 원하는 스타일은 알지만 정확한 `voice_id`를 모를 때 `recommend_voices`를 사용합니다. ```rust let voices = client .recommend_voices("warm female voice for a product tutorial", Some(3)) .await?; for voice in voices { println!("{} {} {}", voice.voice_id, voice.voice_name, voice.score); } ``` 추천 결과에는 `voice_id`, `voice_name`, `score`만 포함됩니다. 최신 보이스 메타데이터가 필요하면 `get_voice_v3` 또는 `get_voices_v3`로 조회하세요. ## 구성 환경 변수 또는 생성자를 통해 API 키를 설정하세요: ```rust use typecast_rust::{TypecastClient, ClientConfig}; use std::time::Duration; // 환경 변수 사용 (권장) // export TYPECAST_API_KEY="your-api-key-here" let client = TypecastClient::from_env()?; // 또는 직접 전달 let client = TypecastClient::with_api_key("your-api-key-here")?; // 또는 사용자 정의 구성과 함께 let config = ClientConfig::new("your-api-key-here") .base_url("https://api.typecast.ai") .timeout(Duration::from_secs(120)); let client = TypecastClient::new(config)?; ``` 자체 프록시를 통해 요청하는 경우 `base_url`을 프록시 엔드포인트로 설정하고 API 키를 생략할 수 있습니다. API 키가 비어 있거나 없으면 SDK는 `X-API-KEY` 헤더를 보내지 않습니다. 기본 Typecast 호스트로 요청할 때는 API 키가 계속 필요합니다. ```rust API 키 없는 프록시 let config = ClientConfig::new("") .base_url("https://your-proxy.example.com"); let client = TypecastClient::new(config)?; ``` ### 환경 파일 프로젝트 루트에 `.env` 파일을 만드세요: ```bash TYPECAST_API_KEY=your-api-key-here ``` `.env` 파일에서 환경 변수를 로드하려면 `dotenvy` 크레이트를 사용하세요. ## 고급 사용법 ### 감정 제어 (ssfm-v30) ssfm-v30은 두 가지 감정 제어 모드를 제공합니다: **프리셋** 및 **스마트**. AI가 문맥에서 감정을 추론하도록 합니다: ```rust use typecast_rust::{TTSRequest, TTSModel, SmartPrompt}; let request = TTSRequest::new( "tc_672c5f5ce59fac2a48faeaee", "모든 것이 잘 될 거예요.", TTSModel::SsfmV30, ) .prompt( SmartPrompt::new() .previous_text("방금 최고의 소식을 들었어요!") // 선택적 문맥 .next_text("축하할 수 있어서 너무 기다려져요!") // 선택적 문맥 ); let response = client.text_to_speech(&request).await?; ``` 프리셋 값으로 감정을 명시적으로 설정합니다: ```rust use typecast_rust::{TTSRequest, TTSModel, PresetPrompt, EmotionPreset}; let request = TTSRequest::new( "tc_672c5f5ce59fac2a48faeaee", "이 기능들을 보여드리게 되어 정말 기대됩니다!", TTSModel::SsfmV30, ) .prompt( PresetPrompt::new() .emotion_preset(EmotionPreset::Happy) // Normal, Happy, Sad, Angry, Whisper, ToneUp, ToneDown .emotion_intensity(1.5) // 범위: 0.0 ~ 2.0 ); let response = client.text_to_speech(&request).await?; ``` ### 오디오 사용자 정의 라우드니스, 피치, 템포 및 출력 형식을 제어합니다: ```rust use typecast_rust::{TTSRequest, TTSModel, Output, AudioFormat}; let request = TTSRequest::new( "tc_672c5f5ce59fac2a48faeaee", "사용자 정의 오디오 출력!", TTSModel::SsfmV30, ) .output( Output::new() .target_lufs(-14.0) // 범위: -70 ~ 0 (LUFS) .audio_pitch(2) // 범위: -12 to +12 반음 .audio_tempo(1.2) // 범위: 0.5x to 2.0x .audio_format(AudioFormat::Mp3) // 옵션: Wav, Mp3 ) .seed(42); // 부호 없는 정수 시드 (재현 가능한 결과) let response = client.text_to_speech(&request).await?; fs::write("output.mp3", &response.audio_data)?; println!("Duration: {:.2}s, Format: {:?}", response.duration, response.format); ``` ### 파일로 바로 생성하기 `generate_to_file`은 음성 합성과 파일 저장을 한 번에 처리합니다. `model`은 기본값으로 `ssfm-v30`을 사용하고, `.mp3` 또는 `.wav` 확장자로 출력 형식을 결정합니다. ```rust client.generate_to_file( "output.mp3", GenerateToFileRequest::new("tc_672c5f5ce59fac2a48faeaee", "안녕하세요, 타입캐스트입니다."), // voice_id는 https://studio.typecast.ai/developers/api/voices 에서 확인하세요. ).await?; ``` ### 텍스트만으로 쉼 표현 한 voice로 읽는 문장 안에 쉼만 넣고 싶다면 텍스트에 pause markup을 직접 작성합니다. `<|5s|>`, `<|1s|>`, `<|0.3s|>`, `<|0.34413s|>`처럼 쓰며 값은 초 단위이고 반드시 `s`로 끝납니다. 별도 pause 함수를 호출하지 않아도 텍스트만 보고 쉼 위치를 확인할 수 있습니다. ```rust let audio = client .compose_speech() .defaults(ComposerSettings::new().voice_id("tc_672c5f5ce59fac2a48faeaee").model(TtsModel::SsfmV30)) .say("안녕하세요<|5s|>반갑습니다<|1s|>오늘<|2s|>날씨는 어떤 것 같으세요?") .generate() .await?; ``` ### 다중 화자 합성 한 파일 안에서 서로 다른 voice나 구간별 pitch, tempo, prompt, seed 같은 옵션을 조합해야 할 때 사용합니다. composer는 세그먼트를 `POST /v1/text-to-speech/compose`로 보내며 WAV 또는 MP3를 직접 반환합니다. 무음 제거는 TTS 세그먼트에 명시적으로 설정하고, 명시적인 쉼은 유지됩니다. ```rust use typecast_rust::{ComposerSettings, Output, TTSModel, TypecastClient}; #[tokio::main] async fn main() -> Result<(), Box> { let client = TypecastClient::new("YOUR_API_KEY")?; let audio = client .compose_speech() .defaults(ComposerSettings::new().voice_id("tc_672c5f5ce59fac2a48faeaee").model(TTSModel::SSFM_V30)) .say("Hello there") .pause(5.0) .say_with( "Nice to meet you", ComposerSettings::new().voice_id("tc_60e5426de8b95f1d3000d7b5").output(Output { audio_pitch: Some(2), ..Default::default() }), ) .say("Today") .pause(2) .say("How does the weather feel?") .generate() .await?; std::fs::write("conversation.wav", audio.audio_data)?; Ok(()) } ``` ## 무음 길이 조절 이 기능은 **0.3.15 이상**에서 지원합니다. `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. 일반·스트리밍·타임스탬프 TTS는 `output.remove_silence_ms`, Compose는 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 전달합니다. 반환 타임스탬프는 처리 후 오디오를 기준으로 하며, 명시적인 `pause` 세그먼트는 유지됩니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. 아래 출력 설정을 해당 요청의 `output`에 전달하세요. 스트리밍은 스트리밍 전용 출력 타입을 사용합니다. ```rust use typecast_rust::{Output, OutputStream}; let output = Output::new().remove_silence_ms(300); let stream_output = OutputStream::new().remove_silence_ms(300); ``` ### 음성 탐색 (V3 API) 향상된 메타데이터로 사용 가능한 음성을 나열하고 필터링합니다: ```rust use typecast_rust::{TypecastClient, VoicesV2Filter, TTSModel, Gender, Age}; // 모든 음성 가져오기 let voices = client.get_voices_v3(None).await?; // 기준으로 필터링 let filter = VoicesV2Filter::new() .model(TTSModel::SsfmV30) .gender(Gender::Female) .age(Age::YoungAdult); let filtered = client.get_voices_v3(Some(filter)).await?; // 음성 정보 표시 for voice in &voices { println!("ID: {}, Name: {}", voice.voice_id, voice.voice_name.kor); println!("Gender: {:?}, Age: {:?}", voice.gender, voice.age); for model in &voice.models { println!("Model: {:?}, Emotions: {:?}", model.version, model.emotions); } if let Some(use_cases) = &voice.use_cases { println!("Use cases: {}", use_cases.join(", ")); } } // ID로 특정 음성 가져오기 let voice = client.get_voice_v3("tc_672c5f5ce59fac2a48faeaee").await?; println!("Voice: {} ({:?})", voice.voice_name.kor, voice.gender); ``` ### 다국어 콘텐츠 SDK는 자동 언어 감지와 함께 35+개 언어를 지원합니다: ```rust // 언어 자동 감지 (권장) let request = TTSRequest::new( "tc_672c5f5ce59fac2a48faeaee", "こんにちは。お元気ですか。", TTSModel::SsfmV30, ); let response = client.text_to_speech(&request).await?; // 또는 명시적으로 언어 지정 let korean_request = TTSRequest::new( "tc_672c5f5ce59fac2a48faeaee", "안녕하세요. 반갑습니다.", TTSModel::SsfmV30, ) .language("kor"); // ISO 639-3 언어 코드 let korean_response = client.text_to_speech(&korean_request).await?; ``` ### 스트리밍 저지연 재생을 위한 실시간 오디오 청크 스트리밍: ```rust // 원시 PCM 추출 (44바이트 WAV 헤더 건너뛰기) let mut stream = client.text_to_speech_stream(&request).await?; let mut first = true; while let Some(chunk) = stream.next().await { let bytes = chunk?; let pcm = if first { first = false; &bytes[44..] // WAV 헤더 건너뛰기 } else { &bytes }; // pcm은 32000 Hz 16비트 모노 원시 PCM // 오디오 출력으로 전달 (예: rodio, cpal) } ``` **WAV 스트리밍 형식:** 32000 Hz, 16비트, 모노 PCM. 첫 번째 청크에 44바이트 WAV 헤더(size = `0xFFFFFFFF`)가 포함되며, 이후 청크는 원시 PCM 데이터만 포함합니다. MP3 형식: 320 kbps, 44100 Hz, 각 청크는 독립적으로 디코딩 가능합니다. `StreamExt`를 사용하려면 `futures-util`이 필요합니다. ## 타임스탬프 TTS `text_to_speech_with_timestamps()`는 `POST /v1/text-to-speech/with-timestamps`를 래핑하며, 오디오와 함께 단어·문자 단위 정렬 데이터를 반환합니다. 가라오케 하이라이트, 자막 생성, 립싱크 애플리케이션에 활용할 수 있습니다. ### 기본 사용법 ```rust use typecast::{TypecastClient, models::TTSRequestWithTimestamps}; use std::fs; #[tokio::main] async fn main() -> Result<(), Box> { let client = TypecastClient::new("YOUR_API_KEY"); let result = client.text_to_speech_with_timestamps(TTSRequestWithTimestamps { voice_id: "tc_60e5426de8b95f1d3000d7b5".to_string(), text: "Hello. How are you?".to_string(), model: "ssfm-v30".to_string(), ..Default::default() }).await?; fs::write("output.wav", result.audio_bytes())?; println!("재생 시간: {:.3}초", result.audio_duration); for word in &result.words { println!(" [{:.3}s – {:.3}s] {}", word.start_time, word.end_time, word.text); } Ok(()) } ``` ### 정밀도(Granularity) 설정 `Granularity::Word`(기본값) 또는 `Granularity::Char`를 설정해 정렬 단위를 제어합니다. ```rust use typecast::models::Granularity; // 문자 단위 정렬 - 일본어·중국어에 필수 let result = client.text_to_speech_with_timestamps(TTSRequestWithTimestamps { voice_id: "tc_60e5426de8b95f1d3000d7b5".to_string(), text: "Hello. How are you?".to_string(), model: "ssfm-v30".to_string(), granularity: Some(Granularity::Char), ..Default::default() }).await?; ``` ### 자막 내보내기 ```rust let srt = result.to_srt()?; fs::write("output.srt", srt)?; let vtt = result.to_vtt()?; fs::write("output.vtt", vtt)?; ``` **일본어·중국어:** 공백 구분자가 없는 언어(jpn, zho)는 단어 단위 세그먼트가 의미를 갖지 않습니다. 해당 언어에는 `Granularity::Char`를 사용해 문자 단위 정렬 데이터를 얻으세요. ## 지원 언어 SDK는 자동 언어 감지와 함께 35+개 언어를 지원합니다: | 코드 | 언어 | 코드 | 언어 | 코드 | 언어 | |------|----------|------|----------|------|----------| | `eng` | 영어 | `jpn` | 일본어 | `ukr` | 우크라이나어 | | `kor` | 한국어 | `ell` | 그리스어 | `ind` | 인도네시아어 | | `spa` | 스페인어 | `tam` | 타밀어 | `dan` | 덴마크어 | | `deu` | 독일어 | `tgl` | 타갈로그어 | `swe` | 스웨덴어 | | `fra` | 프랑스어 | `fin` | 핀란드어 | `msa` | 말레이어 | | `ita` | 이탈리아어 | `zho` | 중국어 | `ces` | 체코어 | | `pol` | 폴란드어 | `slk` | 슬로바키아어 | `por` | 포르투갈어 | | `nld` | 네덜란드어 | `ara` | 아랍어 | `bul` | 불가리아어 | | `rus` | 러시아어 | `hrv` | 크로아티아어 | `ron` | 루마니아어 | | `ben` | 벵골어 | `hin` | 힌디어 | `hun` | 헝가리어 | | `nan` | 민난어 | `nor` | 노르웨이어 | `pan` | 펀자브어 | | `tha` | 태국어 | `tur` | 터키어 | `vie` | 베트남어 | | `yue` | 광둥어 | | | | | 지정하지 않으면 입력 텍스트에서 언어가 자동으로 감지됩니다. ## 오류 처리 SDK는 패턴 매칭으로 API 오류를 처리할 수 있는 타입화된 에러 enum을 제공합니다: ```rust use typecast_rust::{TypecastClient, TTSRequest, TTSModel, TypecastError}; let request = TTSRequest::new("voice_id", "안녕하세요", TTSModel::SsfmV30); match client.text_to_speech(&request).await { Ok(response) => { println!("Success! Duration: {:.2}s", response.duration); } Err(TypecastError::Unauthorized { detail }) => { // 401: 잘못된 API 키 eprintln!("Invalid API key: {}", detail); } Err(TypecastError::PaymentRequired { detail }) => { // 402: 크레딧 부족 eprintln!("Insufficient credits: {}", detail); } Err(TypecastError::NotFound { detail }) => { // 404: 리소스를 찾을 수 없음 eprintln!("Voice not found: {}", detail); } Err(TypecastError::RateLimited { detail }) => { // 429: 요청 한도 초과 eprintln!("Rate limit exceeded - please try again later: {}", detail); } Err(TypecastError::ServerError { detail }) => { // 500: 서버 오류 eprintln!("Server error: {}", detail); } Err(e) => { eprintln!("Error: {}", e); } } ``` ### 오류 유형 | 오류 변형 | 상태 코드 | 설명 | |---------------|-------------|-------------| | `BadRequest` | 400 | 잘못된 요청 매개변수 | | `Unauthorized` | 401 | 잘못되거나 누락된 API 키 | | `PaymentRequired` | 402 | 크레딧 부족 | | `Forbidden` | 403 | 접근 거부 | | `NotFound` | 404 | 리소스를 찾을 수 없음 | | `ValidationError` | 422 | 유효성 검사 오류 | | `RateLimited` | 429 | 요청 한도 초과 | | `ServerError` | 500 | 서버 오류 | | `HttpError` | - | HTTP 클라이언트 오류 | | `JsonError` | - | JSON 직렬화 오류 | ### 헬퍼 메서드 ```rust if let Err(e) = result { if e.is_unauthorized() { println!("Please check your API key"); } else if e.is_rate_limited() { println!("Please try again later"); } else if e.is_server_error() { println!("Server issue, please try again later"); } if let Some(code) = e.status_code() { println!("HTTP status: {}", code); } } ``` ## API 레퍼런스 ### TypecastClient 메서드 | 메서드 | 설명 | |--------|-------------| | `from_env()` | 환경 변수에서 클라이언트 생성 | | `with_api_key(key)` | API 키로 클라이언트 생성 | | `new(config)` | 사용자 정의 구성으로 클라이언트 생성 | | `text_to_speech(&request)` | 텍스트를 음성 오디오로 변환 | | `generate_to_file(path, request)` | 음성을 생성하고 로컬 파일로 바로 저장 | | `get_voices_v3(filter)` | 선택적 필터로 사용 가능한 음성 가져오기 | | `get_voice_v3(voice_id)` | ID로 특정 음성 가져오기 | ### TTSRequest 필드 | 필드 | 타입 | 필수 | 설명 | |-------|------|----------|-------------| | `voice_id` | `String` | ✓ | 음성 ID (형식: `tc_*` 또는 `uc_*`) | | `text` | `String` | ✓ | 합성할 텍스트 (최대 2000자) | | `model` | `TTSModel` | ✓ | TTS 모델 (`SsfmV21` 또는 `SsfmV30`) | | `language` | `Option` | | ISO 639-3 코드 (생략 시 자동 감지) | | `prompt` | `Option` | | 감정 설정 (Prompt/PresetPrompt/SmartPrompt) | | `output` | `Option` | | 오디오 출력 설정 | | `seed` | `Option` | | 재현성을 위한 부호 없는 정수 시드 (≥ 0) | ### TTSResponse 필드 | 필드 | 타입 | 설명 | |-------|------|-------------| | `audio_data` | `Vec` | 생성된 오디오 데이터 | | `duration` | `f64` | 오디오 길이 (초) | | `format` | `AudioFormat` | 오디오 형식 (`Wav` 또는 `Mp3`) | ## 완전한 예제 ```rust use typecast_rust::{ TypecastClient, TTSRequest, TTSModel, PresetPrompt, EmotionPreset, Output, AudioFormat, VoicesV2Filter, Gender, }; use std::fs; use std::error::Error; #[tokio::main] async fn main() -> Result<(), Box> { // 클라이언트 초기화 let client = TypecastClient::from_env()?; // 음성 탐색 let filter = VoicesV2Filter::new() .model(TTSModel::SsfmV30) .gender(Gender::Female); let voices = client.get_voices_v3(Some(filter)).await?; println!("Found {} female voices", voices.len()); // 첫 번째 음성 사용 if let Some(voice) = voices.first() { let request = TTSRequest::new( &voice.voice_id, "Typecast에 오신 것을 환영합니다! 텍스트 음성 변환 API의 데모입니다.", TTSModel::SsfmV30, ) .language("kor") .prompt( PresetPrompt::new() .emotion_preset(EmotionPreset::Happy) .emotion_intensity(1.2) ) .output( Output::new() .target_lufs(-14.0) .audio_format(AudioFormat::Mp3) ); let response = client.text_to_speech(&request).await?; fs::write("welcome.mp3", &response.audio_data)?; println!("welcome.mp3 saved ({:.2}s)", response.duration); } Ok(()) } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # C#/.NET [타입캐스트 API](https://typecast.ai)를 위한 공식 C# 라이브러리입니다. AI 기반 음성을 사용하여 텍스트를 생동감 있는 음성으로 변환하세요. .NET Standard 2.0+, .NET 6+, Unity(NuGetForUnity를 통해), Blazor 애플리케이션을 지원합니다. 동기 대안과 함께 완전한 async/await를 지원합니다. NuGet의 Typecast C# SDK Typecast C# SDK 소스 코드 ## 사전 요구 사항 ### .NET SDK 설치 **Homebrew 사용 (권장)** ```bash # .NET 8 SDK 설치 brew install dotnet@8 # PATH에 추가 export PATH="/opt/homebrew/opt/dotnet@8/bin:$PATH" # 설치 확인 dotnet --version ``` **공식 설치 프로그램 사용** [dotnet.microsoft.com/download](https://dotnet.microsoft.com/download)에서 다운로드하고 `.pkg` 설치 프로그램을 실행하세요. **winget 사용** ```powershell winget install Microsoft.DotNet.SDK.8 dotnet --version ``` **Chocolatey 사용** ```powershell choco install dotnet-sdk dotnet --version ``` 또는 [dotnet.microsoft.com/download](https://dotnet.microsoft.com/download)에서 다운로드하세요. ```bash # Ubuntu/Debian wget https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb sudo dpkg -i packages-microsoft-prod.deb sudo apt-get update sudo apt-get install -y dotnet-sdk-8.0 dotnet --version ``` ## 설치 ```bash dotnet add package typecast-csharp ``` ```powershell Install-Package typecast-csharp ``` 1. [NuGetForUnity](https://github.com/GlitchEnzo/NuGetForUnity) 설치: - Package Manager 열기 (Window > Package Manager) - "+" 클릭 > "Add package from git URL" - 입력: `https://github.com/GlitchEnzo/NuGetForUnity.git?path=/src/NuGetForUnity` 2. NuGet 창 열기 (NuGet > Manage NuGet Packages) 3. "typecast-csharp" 검색 후 설치 최신 등록 버전은 NuGet 기준 **0.3.13**입니다. `dotnet list package`로 확인할 수 있습니다. `dotnet add package typecast-csharp`로 최신 버전을 가져오세요. ## 빠른 시작 ```csharp using Typecast; using Typecast.Models; // 클라이언트 초기화 using var client = new TypecastClient("YOUR_API_KEY"); // 텍스트를 음성으로 변환 var request = new TTSRequest( text: "안녕하세요! 저는 텍스트 음성 변환 에이전트입니다.", voiceId: "tc_672c5f5ce59fac2a48faeaee", model: TTSModel.SsfmV30 ); var response = await client.TextToSpeechAsync(request); // 오디오 파일 저장 await response.SaveToFileAsync("output.wav"); Console.WriteLine($"Audio saved! Duration: {response.Duration}s, Format: {response.Format}"); ``` ## 기능 Typecast C# SDK는 텍스트 음성 변환을 위한 강력한 기능을 제공합니다: - **다중 음성 모델**: `ssfm-v30`(최신) 및 `ssfm-v21` AI 음성 모델 지원 - **다국어 지원**: 영어, 한국어, 스페인어, 일본어, 중국어 등 35+개 언어 지원 - **감정 제어**: 이모션 프리셋(normal, happy, sad, angry, whisper, toneup, tonedown) 또는 스마트 문맥 인식 추론 - **오디오 사용자 정의**: 라우드니스 (LUFS -70 to 0), 피치(-12 to +12 반음), 템포(0.5x to 2.0x), 형식(WAV/MP3) 제어 - **음성 탐색**: 모델, 성별, 나이, 사용 사례별 필터링이 가능한 V2 Voices API - **Unity 지원**: NuGetForUnity를 통해 Unity와 호환 - **Blazor 지원**: Blazor Server 및 WebAssembly와 함께 작동 - **타임스탬프 TTS**: 자막, 가라오케, 립싱크를 위한 단어·문자 단위 정렬 데이터 - **스트리밍**: 저지연 재생을 위한 실시간 청크 오디오 전송 - **Async/Sync API**: 동기 대안과 함께 완전한 async/await 지원 ## 보이스 추천 원하는 스타일은 알지만 정확한 `voice_id`를 모를 때 `RecommendVoicesAsync`를 사용합니다. ```csharp var voices = await client.RecommendVoicesAsync( "warm female voice for a product tutorial", count: 3 ); foreach (var voice in voices) { Console.WriteLine($"{voice.VoiceId} {voice.VoiceName} {voice.Score}"); } ``` 추천 결과에는 `VoiceId`, `VoiceName`, `Score`만 포함됩니다. 지원 모델, 감정, 성별, 연령대, 사용 사례 같은 상세 메타데이터가 필요하면 `GetVoiceV2Async` 또는 `GetVoicesV2Async`로 추가 조회하세요. ## 구성 환경 변수 또는 생성자를 통해 API 키를 설정하세요: ```csharp // 환경 변수 사용 (TYPECAST_API_KEY) using var client = new TypecastClient(); // 또는 직접 전달 using var client = new TypecastClient("your-api-key-here"); // 또는 구성 객체 사용 var config = new TypecastClientConfig { ApiKey = "your-api-key-here", TimeoutSeconds = 60 // 선택 사항, 기본값: 30 }; using var client = new TypecastClient(config); ``` 자체 프록시를 통해 요청하는 경우 `ApiHost`를 프록시 엔드포인트로 설정하고 `ApiKey`를 생략할 수 있습니다. API 키가 비어 있거나 없으면 SDK는 `X-API-KEY` 헤더를 보내지 않습니다. 기본 Typecast 호스트로 요청할 때는 API 키가 계속 필요합니다. ```csharp API 키 없는 프록시 var config = new TypecastClientConfig { ApiHost = "https://your-proxy.example.com" }; using var client = new TypecastClient(config); ``` ## 고급 사용법 ### 감정 제어 (ssfm-v30) ssfm-v30은 두 가지 감정 제어 모드를 제공합니다: **프리셋** 및 **스마트**. AI가 문맥에서 감정을 추론하도록 합니다: ```csharp var request = new TTSRequest("모든 것이 잘 될 거예요.", voiceId, TTSModel.SsfmV30) { Language = LanguageCode.Korean, Prompt = new SmartPrompt( previousText: "방금 최고의 소식을 들었어요!", nextText: "축하할 수 있어서 너무 기다려져요!" ) }; var response = await client.TextToSpeechAsync(request); ``` 프리셋 값으로 감정을 명시적으로 설정합니다: ```csharp var request = new TTSRequest("이 기능들을 보여드리게 되어 정말 기대됩니다!", voiceId, TTSModel.SsfmV30) { Language = LanguageCode.Korean, Prompt = new PresetPrompt( emotionPreset: EmotionPreset.Happy, emotionIntensity: 1.5 // 범위: 0.0 ~ 2.0 ) }; var response = await client.TextToSpeechAsync(request); ``` ### 오디오 사용자 정의 라우드니스, 피치, 템포 및 출력 형식을 제어합니다: ```csharp var request = new TTSRequest("사용자 정의 오디오 출력!", voiceId, TTSModel.SsfmV30) { Language = LanguageCode.Korean, Output = new Output( targetLufs: -14.0, // 범위: -70 ~ 0 (LUFS) audioPitch: 2, // 범위: -12 to +12 반음 audioTempo: 1.2, // 범위: 0.5x to 2.0x audioFormat: AudioFormat.Mp3 // 옵션: Wav, Mp3 ), Seed = 42 // 재현 가능한 결과를 위해 }; var response = await client.TextToSpeechAsync(request); await response.SaveToFileAsync($"output{response.FileExtension}"); Console.WriteLine($"Duration: {response.Duration}s, Format: {response.Format}"); ``` ### 파일로 바로 생성하기 `GenerateToFileAsync`는 음성 합성과 파일 저장을 한 번에 처리합니다. `Model`은 기본값으로 `ssfm-v30`을 사용하고, `.mp3` 또는 `.wav` 확장자로 출력 형식을 결정합니다. ```csharp await client.GenerateToFileAsync("output.mp3", new GenerateToFileRequest { Text = "안녕하세요, 타입캐스트입니다.", VoiceId = "tc_672c5f5ce59fac2a48faeaee" // voice_id는 https://studio.typecast.ai/developers/api/voices 에서 확인하세요. }); ``` ### 텍스트만으로 쉼 표현 한 voice로 읽는 문장 안에 쉼만 넣고 싶다면 텍스트에 pause markup을 직접 작성합니다. `<|5s|>`, `<|1s|>`, `<|0.3s|>`, `<|0.34413s|>`처럼 쓰며 값은 초 단위이고 반드시 `s`로 끝납니다. 별도 pause 함수를 호출하지 않아도 텍스트만 보고 쉼 위치를 확인할 수 있습니다. ```csharp var audio = await client.ComposeSpeech() .Defaults(new ComposerSettings { VoiceId = "tc_672c5f5ce59fac2a48faeaee", Model = TTSModel.SsfmV30 }) .Say("안녕하세요<|5s|>반갑습니다<|1s|>오늘<|2s|>날씨는 어떤 것 같으세요?") .GenerateAsync(); ``` ### 다중 화자 합성 한 파일 안에서 서로 다른 voice나 구간별 pitch, tempo, prompt, seed 같은 옵션을 조합해야 할 때 사용합니다. composer는 세그먼트를 `POST /v1/text-to-speech/compose`로 보내며 WAV 또는 MP3를 직접 반환합니다. 무음 제거는 TTS 세그먼트에 명시적으로 설정하고, 명시적인 쉼은 유지됩니다. ```csharp using Typecast; using Typecast.Models; using var client = new TypecastClient("YOUR_API_KEY"); var audio = await client.ComposeSpeech() .Defaults(new ComposerSettings { VoiceId = "tc_672c5f5ce59fac2a48faeaee", Model = TTSModel.SsfmV30 }) .Say("Hello there") .Pause(5) .Say("Nice to meet you", new ComposerSettings { VoiceId = "tc_60e5426de8b95f1d3000d7b5", Output = new Output(audioPitch: 2) }) .Say("Today") .Pause(2) .Say("How does the weather feel?") .GenerateAsync(); await audio.SaveToFileAsync("conversation.wav"); ``` ### 음성 탐색 (V2 API) 향상된 메타데이터로 사용 가능한 음성을 나열하고 필터링합니다: ```csharp // 모든 음성 가져오기 var voices = await client.GetVoicesV2Async(); // 기준으로 필터링 var filtered = await client.GetVoicesV2Async(new VoicesV2Filter { Model = TTSModel.SsfmV30, Gender = GenderEnum.Female, Age = AgeEnum.YoungAdult }); // 음성 정보 표시 foreach (var voice in voices) { Console.WriteLine($"ID: {voice.VoiceId}, Name: {voice.VoiceName}"); Console.WriteLine($"Gender: {voice.Gender}, Age: {voice.Age}"); Console.WriteLine($"Models: {string.Join(", ", voice.Models.Select(m => m.Version))}"); Console.WriteLine($"Use cases: {string.Join(", ", voice.UseCases ?? new List())}"); } ``` ### 스트리밍 저지연 재생을 위한 실시간 오디오 청크 스트리밍: ```csharp // 원시 PCM 추출 (44바이트 WAV 헤더 건너뛰기) using var stream = await client.TextToSpeechStreamAsync(request); var buffer = new byte[8192]; bool first = true; while (true) { int bytesRead = await stream.ReadAsync(buffer); if (bytesRead == 0) break; ReadOnlySpan pcm = buffer.AsSpan(0, bytesRead); if (first) { pcm = pcm[44..]; // WAV 헤더 건너뛰기 first = false; } // pcm은 32000 Hz 16비트 모노 원시 PCM // 오디오 출력으로 전달 (예: NAudio) } ``` **WAV 스트리밍 형식:** 32000 Hz, 16비트, 모노 PCM. 첫 번째 청크에 44바이트 WAV 헤더(size = `0xFFFFFFFF`)가 포함되며, 이후 청크는 원시 PCM 데이터만 포함합니다. MP3 형식: 320 kbps, 44100 Hz, 각 청크는 독립적으로 디코딩 가능합니다. ## 타임스탬프 TTS `TextToSpeechWithTimestampsAsync()`는 `POST /v1/text-to-speech/with-timestamps`를 래핑하며, 오디오와 함께 단어·문자 단위 정렬 데이터를 반환합니다. 가라오케 하이라이트, 자막 생성, 립싱크 애플리케이션에 활용할 수 있습니다. ### 기본 사용법 ```csharp using Typecast; using Typecast.Models; var client = new TypecastClient("YOUR_API_KEY"); var result = await client.TextToSpeechWithTimestampsAsync(new TTSRequestWithTimestamps { VoiceId = "tc_60e5426de8b95f1d3000d7b5", Text = "Hello. How are you?", Model = "ssfm-v30" }); await File.WriteAllBytesAsync("output.wav", result.AudioBytes()); Console.WriteLine($"재생 시간: {result.AudioDuration:F3}초"); foreach (var word in result.Words) { Console.WriteLine($" [{word.StartTime:F3}s – {word.EndTime:F3}s] {word.Text}"); } ``` ### 정밀도(Granularity) 설정 `Granularity = Granularity.Word`(기본값) 또는 `Granularity = Granularity.Char`를 설정해 정렬 단위를 제어합니다. ```csharp // 문자 단위 정렬 - 일본어·중국어에 필수 var result = await client.TextToSpeechWithTimestampsAsync(new TTSRequestWithTimestamps { VoiceId = "tc_60e5426de8b95f1d3000d7b5", Text = "Hello. How are you?", Model = "ssfm-v30", Granularity = Granularity.Char }); ``` ### 자막 내보내기 ```csharp string srt = result.ToSrt(); await File.WriteAllTextAsync("output.srt", srt); string vtt = result.ToVtt(); await File.WriteAllTextAsync("output.vtt", vtt); ``` **일본어·중국어:** 공백 구분자가 없는 언어(jpn, zho)는 단어 단위 세그먼트가 의미를 갖지 않습니다. 해당 언어에는 `Granularity.Char`를 사용해 문자 단위 정렬 데이터를 얻으세요. ## 지원 언어 SDK는 자동 언어 감지와 함께 35+개 언어를 지원합니다: | 코드 | 언어 | 코드 | 언어 | 코드 | 언어 | |------|----------|------|----------|------|----------| | `eng` | 영어 | `jpn` | 일본어 | `ukr` | 우크라이나어 | | `kor` | 한국어 | `ell` | 그리스어 | `ind` | 인도네시아어 | | `spa` | 스페인어 | `tam` | 타밀어 | `dan` | 덴마크어 | | `deu` | 독일어 | `tgl` | 타갈로그어 | `swe` | 스웨덴어 | | `fra` | 프랑스어 | `fin` | 핀란드어 | `msa` | 말레이어 | | `ita` | 이탈리아어 | `zho` | 중국어 | `ces` | 체코어 | | `pol` | 폴란드어 | `slk` | 슬로바키아어 | `por` | 포르투갈어 | | `nld` | 네덜란드어 | `ara` | 아랍어 | `bul` | 불가리아어 | | `rus` | 러시아어 | `hrv` | 크로아티아어 | `ron` | 루마니아어 | | `ben` | 벵골어 | `hin` | 힌디어 | `hun` | 헝가리어 | | `nan` | 민난어 | `nor` | 노르웨이어 | `pan` | 펀자브어 | | `tha` | 태국어 | `tur` | 터키어 | `vie` | 베트남어 | | `yue` | 광둥어 | | | | | 지정하지 않으면 입력 텍스트에서 언어가 자동으로 감지됩니다. ## 오류 처리 SDK는 API 오류 처리를 위한 특정 예외 타입을 제공합니다: ```csharp using Typecast; using Typecast.Exceptions; try { var response = await client.TextToSpeechAsync(request); } catch (UnauthorizedException) { Console.WriteLine("Invalid or missing API key"); } catch (PaymentRequiredException) { Console.WriteLine("Insufficient credits"); } catch (UnprocessableEntityException ex) { Console.WriteLine($"Validation error: {ex.ResponseBody}"); } catch (RateLimitException) { Console.WriteLine("Rate limit exceeded - please try again later"); } catch (TypecastException ex) { Console.WriteLine($"API error ({ex.StatusCode}): {ex.Message}"); } ``` ## 동기 API async가 선호되지 않는 시나리오의 경우 동기 메서드를 사용하세요: ```csharp // 동기 텍스트 음성 변환 var response = client.TextToSpeech(request); response.SaveToFile("output.wav"); // 동기 음성 목록 var voices = client.GetVoicesV2(); var voice = client.GetVoiceV2("voice_id"); ``` ## 무음 길이 조절 이 기능은 **0.3.13 이상**에서 지원합니다. `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. 일반·스트리밍·타임스탬프 TTS는 `output.remove_silence_ms`, Compose는 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 전달합니다. 반환 타임스탬프는 처리 후 오디오를 기준으로 하며, 명시적인 `pause` 세그먼트는 유지됩니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. 아래 출력 설정을 해당 요청의 `output`에 전달하세요. 스트리밍은 스트리밍 전용 출력 타입을 사용합니다. ```csharp var output = new Typecast.Models.Output { RemoveSilenceMs = 300 }; var streamOutput = new Typecast.Models.OutputStream { RemoveSilenceMs = 300 }; ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Java [타입캐스트 API](https://typecast.ai)를 위한 공식 Java 라이브러리입니다. AI 기반 음성을 사용하여 텍스트를 생동감 있는 음성으로 변환하세요. Java 8 이상 버전과 호환됩니다. Maven, Gradle, 수동 설치와 함께 작동합니다. Typecast Java SDK Typecast Java SDK 소스 코드 ## 설치 `pom.xml`에 다음 의존성을 추가하세요: ```xml com.neosapience typecast-java 1.2.12 ``` `build.gradle`에 추가하세요: ```groovy implementation 'com.neosapience:typecast-java:1.2.12' ``` 로컬 Maven 저장소에 클론하고 설치하세요: ```bash git clone https://github.com/neosapience/typecast-sdk.git cd typecast-sdk/typecast-java mvn clean install -DskipTests ``` 최신 등록 버전은 Maven Central 기준 **1.2.12**입니다. **버전 1.2.12 이상**이 설치되어 있는지 확인하세요. 이전 버전이 있다면 `pom.xml` 또는 `build.gradle`에서 의존성 버전을 업데이트하세요. ## 빠른 시작 ```java import com.neosapience.TypecastClient; import com.neosapience.models.*; import java.io.FileOutputStream; public class QuickStart { public static void main(String[] args) throws Exception { // 클라이언트 초기화 TypecastClient client = new TypecastClient("YOUR_API_KEY"); // 텍스트를 음성으로 변환 TTSRequest request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("안녕하세요! 저는 텍스트 음성 변환 에이전트입니다.") .model(TTSModel.SSFM_V30) .build(); TTSResponse response = client.textToSpeech(request); // 오디오 파일 저장 try (FileOutputStream fos = new FileOutputStream("output." + response.getFormat())) { fos.write(response.getAudioData()); } System.out.println("Audio saved! Duration: " + response.getDuration() + "s, Format: " + response.getFormat()); // 정리 client.close(); } } ``` ## 기능 Typecast Java SDK는 텍스트 음성 변환을 위한 강력한 기능을 제공합니다: - **다중 음성 모델**: `ssfm-v30`(최신) 및 `ssfm-v21` AI 음성 모델 지원 - **다국어 지원**: 영어, 한국어, 스페인어, 일본어, 중국어 등 35+개 언어 지원 - **감정 제어**: 이모션 프리셋(normal, happy, sad, angry, whisper, toneup, tonedown) 또는 스마트 문맥 인식 추론 - **오디오 사용자 정의**: 라우드니스 LUFS(-70 to 0), 피치(-12 to +12 반음), 템포(0.5x to 2.0x), 형식(WAV/MP3) 제어 - **음성 탐색**: 모델, 성별, 나이, 사용 사례별 필터링이 가능한 V2 Voices API - **빌더 패턴**: 쉬운 요청 구성을 위한 빌더 패턴의 Fluent API - **타임스탬프 TTS**: 자막, 가라오케, 립싱크를 위한 단어·문자 단위 정렬 데이터 - **스트리밍**: 저지연 재생을 위한 실시간 청크 오디오 전송 - **포괄적인 오류 처리**: 각 오류 유형에 대한 특정 예외 클래스 ## 보이스 추천 원하는 스타일은 알지만 정확한 `voice_id`를 모를 때 `recommendVoices`를 사용합니다. ```java List voices = client.recommendVoices( "warm female voice for a product tutorial", 3 ); for (RecommendedVoice voice : voices) { System.out.println(voice.getVoiceId() + " " + voice.getVoiceName() + " " + voice.getScore()); } ``` 추천 결과에는 `voiceId`, `voiceName`, `score`만 포함됩니다. 지원 모델, 감정, 성별, 연령대, 사용 사례 같은 상세 메타데이터가 필요하면 `getVoiceV2` 또는 `getVoicesV2`로 추가 조회하세요. ## 구성 환경 변수, `.env` 파일 또는 생성자를 통해 API 키를 설정하세요: ```java // 환경 변수 사용 // export TYPECAST_API_KEY="your-api-key-here" TypecastClient client = new TypecastClient(); // 또는 직접 전달 TypecastClient client = new TypecastClient("your-api-key-here"); // 또는 사용자 정의 base URL과 함께 TypecastClient client = new TypecastClient("your-api-key-here", "https://custom-api.example.com"); ``` 자체 프록시를 통해 요청하는 경우 프록시 base URL을 전달하고 API 키는 `null` 또는 빈 문자열로 생략할 수 있습니다. API 키가 비어 있거나 없으면 SDK는 `X-API-KEY` 헤더를 보내지 않습니다. 기본 Typecast 호스트로 요청할 때는 API 키가 계속 필요합니다. ```java API 키 없는 프록시 TypecastClient client = new TypecastClient(null, "https://your-proxy.example.com"); ``` ### 환경 파일 프로젝트 루트에 `.env` 파일을 만드세요: ```bash TYPECAST_API_KEY=your-api-key-here ``` ## 고급 사용법 ### 감정 제어 (ssfm-v30) ssfm-v30은 두 가지 감정 제어 모드를 제공합니다: **프리셋** 및 **스마트**. AI가 문맥에서 감정을 추론하도록 합니다: ```java TTSRequest request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("모든 것이 잘 될 거예요.") .model(TTSModel.SSFM_V30) .prompt(SmartPrompt.builder() .previousText("방금 최고의 소식을 들었어요!") // 선택적 문맥 .nextText("축하할 수 있어서 너무 기다려져요!") // 선택적 문맥 .build()) .build(); TTSResponse response = client.textToSpeech(request); ``` 프리셋 값으로 감정을 명시적으로 설정합니다: ```java TTSRequest request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("이 기능들을 보여드리게 되어 정말 기대됩니다!") .model(TTSModel.SSFM_V30) .prompt(PresetPrompt.builder() .emotionPreset(EmotionPreset.HAPPY) // normal, happy, sad, angry, whisper, toneup, tonedown .emotionIntensity(1.5) // 범위: 0.0 ~ 2.0 .build()) .build(); TTSResponse response = client.textToSpeech(request); ``` ### 오디오 사용자 정의 라우드니스, 피치, 템포 및 출력 형식을 제어합니다: ```java TTSRequest request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("사용자 정의 오디오 출력!") .model(TTSModel.SSFM_V30) .output(Output.builder() .targetLufs(-14.0) // 범위: -70 ~ 0 (LUFS) .audioPitch(2) // 범위: -12 to +12 반음 .audioTempo(1.2) // 범위: 0.5x to 2.0x .audioFormat(AudioFormat.MP3) // 옵션: WAV, MP3 .build()) .seed(42) // 0 이상의 정수 시드 (재현 가능한 결과) .build(); TTSResponse response = client.textToSpeech(request); try (FileOutputStream fos = new FileOutputStream("output." + response.getFormat())) { fos.write(response.getAudioData()); } System.out.println("Duration: " + response.getDuration() + "s, Format: " + response.getFormat()); ``` ### 파일로 바로 생성하기 `generateToFile`은 음성 합성과 파일 저장을 한 번에 처리합니다. `model`은 기본값으로 `ssfm-v30`을 사용하고, `.mp3` 또는 `.wav` 확장자로 출력 형식을 결정합니다. ```java client.generateToFile("output.mp3", GenerateToFileRequest.builder() .text("안녕하세요, 타입캐스트입니다.") .voiceId("tc_672c5f5ce59fac2a48faeaee") // voice_id는 https://studio.typecast.ai/developers/api/voices 에서 확인하세요. .build()); ``` ### 텍스트만으로 쉼 표현 한 voice로 읽는 문장 안에 쉼만 넣고 싶다면 텍스트에 pause markup을 직접 작성합니다. `<|5s|>`, `<|1s|>`, `<|0.3s|>`, `<|0.34413s|>`처럼 쓰며 값은 초 단위이고 반드시 `s`로 끝납니다. 별도 pause 함수를 호출하지 않아도 텍스트만 보고 쉼 위치를 확인할 수 있습니다. ```java TTSResponse audio = client.composeSpeech() .defaults(new ComposerSettings().voiceId("tc_672c5f5ce59fac2a48faeaee").model(TTSModel.SSFM_V30)) .say("안녕하세요<|5s|>반갑습니다<|1s|>오늘<|2s|>날씨는 어떤 것 같으세요?") .generate(); ``` ### 다중 화자 합성 한 파일 안에서 서로 다른 voice나 구간별 pitch, tempo, prompt, seed 같은 옵션을 조합해야 할 때 사용합니다. composer는 세그먼트를 `POST /v1/text-to-speech/compose`로 보내며 WAV 또는 MP3를 직접 반환합니다. 무음 제거는 TTS 세그먼트에 명시적으로 설정하고, 명시적인 쉼은 유지됩니다. ```java TypecastClient client = new TypecastClient("YOUR_API_KEY"); TTSResponse audio = client.composeSpeech() .defaults(new ComposerSettings().setVoiceId("tc_672c5f5ce59fac2a48faeaee").setModel(TTSModel.SSFM_V30)) .say("Hello there") .pause(5) .say("Nice to meet you", new ComposerSettings() .setVoiceId("tc_60e5426de8b95f1d3000d7b5") .setOutput(Output.builder().volume(null).audioPitch(2).build())) .say("Today") .pause(2) .say("How does the weather feel?") .generate(); Files.write(Path.of("conversation.wav"), audio.getAudioData()); ``` ### 음성 탐색 (V2 API) 향상된 메타데이터로 사용 가능한 음성을 나열하고 필터링합니다: ```java // 모든 음성 가져오기 List voices = client.getVoicesV2(); // 기준으로 필터링 VoicesV2Filter filter = VoicesV2Filter.builder() .model(TTSModel.SSFM_V30) .gender(GenderEnum.FEMALE) .age(AgeEnum.YOUNG_ADULT) .build(); List filtered = client.getVoicesV2(filter); // 음성 정보 표시 for (VoiceV2Response voice : voices) { System.out.println("ID: " + voice.getVoiceId() + ", Name: " + voice.getVoiceName()); System.out.println("Gender: " + voice.getGender() + ", Age: " + voice.getAge()); for (ModelInfo model : voice.getModels()) { System.out.println("Model: " + model.getVersion() + ", Emotions: " + model.getEmotions()); } if (voice.getUseCases() != null) { System.out.println("Use cases: " + String.join(", ", voice.getUseCases())); } } ``` ### 스트리밍 저지연 재생을 위한 실시간 오디오 청크 스트리밍: ```java import javax.sound.sampled.*; // 오디오 재생 설정: 32000 Hz, 16비트, 모노, 리틀엔디안 AudioFormat format = new AudioFormat(32000, 16, 1, true, false); SourceDataLine line = AudioSystem.getSourceDataLine(format); line.open(format, 8192); line.start(); try (InputStream stream = client.textToSpeechStream(request)) { byte[] buf = new byte[4096]; boolean first = true; int bytesRead; while ((bytesRead = stream.read(buf)) != -1) { int offset = 0; if (first) { offset = 44; // 44바이트 WAV 헤더 건너뛰기 bytesRead -= 44; first = false; } line.write(buf, offset, bytesRead); } } line.drain(); line.close(); client.close(); ``` **WAV 스트리밍 형식:** 32000 Hz, 16비트, 모노 PCM. 첫 번째 청크에 44바이트 WAV 헤더(size = `0xFFFFFFFF`)가 포함되며, 이후 청크는 원시 PCM 데이터만 포함합니다. MP3 형식: 320 kbps, 44100 Hz, 각 청크는 독립적으로 디코딩 가능합니다. `java.io.OutputStream`과의 이름 충돌을 피하려면 `com.neosapience.models.OutputStream`으로 정규화된 이름을 사용하세요. ## 타임스탬프 TTS `textToSpeechWithTimestamps()`는 `POST /v1/text-to-speech/with-timestamps`를 래핑하며, 오디오와 함께 단어·문자 단위 정렬 데이터를 반환합니다. 가라오케 하이라이트, 자막 생성, 립싱크 애플리케이션에 활용할 수 있습니다. ### 기본 사용법 ```java import com.neosapience.TypecastClient; import com.neosapience.models.TTSRequestWithTimestamps; import com.neosapience.models.TTSWithTimestampsResponse; import java.nio.file.Files; import java.nio.file.Paths; TypecastClient client = new TypecastClient("YOUR_API_KEY"); TTSWithTimestampsResponse result = client.textToSpeechWithTimestamps( TTSRequestWithTimestamps.builder() .voiceId("tc_60e5426de8b95f1d3000d7b5") .text("Hello. How are you?") .model("ssfm-v30") .build() ); Files.write(Paths.get("output.wav"), result.audioBytes()); System.out.printf("재생 시간: %.3f초%n", result.getAudioDuration()); for (var word : result.getWords()) { System.out.printf(" [%.3fs – %.3fs] %s%n", word.getStartTime(), word.getEndTime(), word.getText()); } ``` ### 정밀도(Granularity) 설정 `granularity("word")`(기본값) 또는 `granularity("char")`을 설정해 정렬 단위를 제어합니다. ```java // 문자 단위 정렬 - 일본어·중국어에 필수 TTSWithTimestampsResponse result = client.textToSpeechWithTimestamps( TTSRequestWithTimestamps.builder() .voiceId("tc_60e5426de8b95f1d3000d7b5") .text("Hello. How are you?") .model("ssfm-v30") .granularity("char") .build() ); ``` ### 자막 내보내기 ```java String srt = result.toSrt(); Files.writeString(Paths.get("output.srt"), srt); String vtt = result.toVtt(); Files.writeString(Paths.get("output.vtt"), vtt); ``` **일본어·중국어:** 공백 구분자가 없는 언어(jpn, zho)는 단어 단위 세그먼트가 의미를 갖지 않습니다. 해당 언어에는 `granularity("char")`를 사용해 문자 단위 정렬 데이터를 얻으세요. ## 지원 언어 SDK는 자동 언어 감지와 함께 35+개 언어를 지원합니다: | 코드 | 언어 | 코드 | 언어 | 코드 | 언어 | |------|----------|------|----------|------|----------| | `ENG` | 영어 | `JPN` | 일본어 | `UKR` | 우크라이나어 | | `KOR` | 한국어 | `ELL` | 그리스어 | `IND` | 인도네시아어 | | `SPA` | 스페인어 | `TAM` | 타밀어 | `DAN` | 덴마크어 | | `DEU` | 독일어 | `TGL` | 타갈로그어 | `SWE` | 스웨덴어 | | `FRA` | 프랑스어 | `FIN` | 핀란드어 | `MSA` | 말레이어 | | `ITA` | 이탈리아어 | `ZHO` | 중국어 | `CES` | 체코어 | | `POL` | 폴란드어 | `SLK` | 슬로바키아어 | `POR` | 포르투갈어 | | `NLD` | 네덜란드어 | `ARA` | 아랍어 | `BUL` | 불가리아어 | | `RUS` | 러시아어 | `HRV` | 크로아티아어 | `RON` | 루마니아어 | | `BEN` | 벵골어 | `HIN` | 힌디어 | `HUN` | 헝가리어 | | `NAN` | 민난어 | `NOR` | 노르웨이어 | `PAN` | 펀자브어 | | `THA` | 태국어 | `TUR` | 터키어 | `VIE` | 베트남어 | | `YUE` | 광둥어 | | | | | 지정하지 않으면 입력 텍스트에서 언어가 자동으로 감지됩니다. ## 오류 처리 SDK는 API 오류 처리를 위한 특정 예외 클래스를 제공합니다: ```java import com.neosapience.TypecastClient; import com.neosapience.exceptions.*; try { TTSResponse response = client.textToSpeech(request); } catch (UnauthorizedException e) { // 401: 잘못된 API 키 System.err.println("Invalid API key: " + e.getMessage()); } catch (PaymentRequiredException e) { // 402: 크레딧 부족 System.err.println("Insufficient credits: " + e.getMessage()); } catch (ForbiddenException e) { // 403: 접근 거부 System.err.println("Access denied: " + e.getMessage()); } catch (NotFoundException e) { // 404: 리소스를 찾을 수 없음 System.err.println("Voice not found: " + e.getMessage()); } catch (UnprocessableEntityException e) { // 422: 유효성 검사 오류 System.err.println("Validation error: " + e.getMessage()); } catch (RateLimitException e) { // 429: 요청 한도 초과 System.err.println("Rate limit exceeded - please try again later"); } catch (InternalServerException e) { // 500: 서버 오류 System.err.println("Server error: " + e.getMessage()); } catch (TypecastException e) { // 일반 오류 System.err.println("API error (" + e.getStatusCode() + "): " + e.getMessage()); } ``` ### 예외 계층 구조 | 예외 | 상태 코드 | 설명 | |-----------|-------------|-------------| | `BadRequestException` | 400 | 잘못된 요청 매개변수 | | `UnauthorizedException` | 401 | 잘못되거나 누락된 API 키 | | `PaymentRequiredException` | 402 | 크레딧 부족 | | `ForbiddenException` | 403 | 접근 거부 | | `NotFoundException` | 404 | 리소스를 찾을 수 없음 | | `UnprocessableEntityException` | 422 | 유효성 검사 오류 | | `RateLimitException` | 429 | 요청 한도 초과 | | `InternalServerException` | 500 | 서버 오류 | | `TypecastException` | * | 기본 예외 클래스 | ## 무음 길이 조절 이 기능은 **1.2.12 이상**에서 지원합니다. `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. 일반·스트리밍·타임스탬프 TTS는 `output.remove_silence_ms`, Compose는 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 전달합니다. 반환 타임스탬프는 처리 후 오디오를 기준으로 하며, 명시적인 `pause` 세그먼트는 유지됩니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. 아래 출력 설정을 해당 요청의 `output`에 전달하세요. 스트리밍은 스트리밍 전용 출력 타입을 사용합니다. ```java Output output = Output.builder().removeSilenceMs(300).build(); OutputStream streamOutput = OutputStream.builder().removeSilenceMs(300).build(); ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Kotlin [타입캐스트 API](https://typecast.ai)를 위한 공식 Kotlin 라이브러리입니다. AI 기반 음성을 사용하여 텍스트를 생동감 있는 음성으로 변환하세요. Kotlin 1.9+ 및 JDK 17 이상 버전과 호환됩니다. Gradle (Kotlin DSL 또는 Groovy) 및 Maven과 함께 작동합니다. Typecast Kotlin SDK Typecast Kotlin SDK 소스 코드 ## 설치 `build.gradle.kts`에 다음 의존성을 추가하세요: ```kotlin dependencies { implementation("com.neosapience:typecast-kotlin:1.2.13") } ``` `build.gradle`에 추가하세요: ```groovy implementation 'com.neosapience:typecast-kotlin:1.2.13' ``` `pom.xml`에 다음 의존성을 추가하세요: ```xml com.neosapience typecast-kotlin 1.2.13 ``` 최신 등록 버전은 Maven Central 기준 **1.2.13**입니다. **버전 1.2.13 이상**이 설치되어 있는지 확인하세요. 이전 버전이 있다면 의존성 버전을 업데이트하세요. ## 빠른 시작 ```kotlin import com.neosapience.TypecastClient import com.neosapience.models.* import java.io.File fun main() { // 클라이언트 초기화 val client = TypecastClient.create("YOUR_API_KEY") // 텍스트를 음성으로 변환 val request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("안녕하세요! 저는 텍스트 음성 변환 에이전트입니다.") .model(TTSModel.SSFM_V30) .build() val response = client.textToSpeech(request) // 오디오 파일 저장 File("output.${response.format}").writeBytes(response.audioData) println("Audio saved! Duration: ${response.duration}s, Format: ${response.format}") // 정리 client.close() } ``` ## 기능 Typecast Kotlin SDK는 텍스트 음성 변환을 위한 강력한 기능을 제공합니다: - **다중 음성 모델**: `ssfm-v30`(최신) 및 `ssfm-v21` AI 음성 모델 지원 - **다국어 지원**: 영어, 한국어, 스페인어, 일본어, 중국어 등 35+개 언어 지원 - **감정 제어**: 이모션 프리셋(normal, happy, sad, angry, whisper, toneup, tonedown) 또는 스마트 문맥 인식 추론 - **오디오 사용자 정의**: 라우드니스 LUFS(-70 to 0), 피치(-12 to +12 반음), 템포(0.5x to 2.0x), 형식(WAV/MP3) 제어 - **음성 탐색**: 모델, 성별, 나이, 사용 사례별 필터링이 가능한 V2 Voices API - **관용적 Kotlin**: data class를 사용한 Kotlin 친화적 빌더 패턴 구문 - **타임스탬프 TTS**: 자막, 가라오케, 립싱크를 위한 단어·문자 단위 정렬 데이터 - **스트리밍**: 저지연 재생을 위한 실시간 청크 오디오 전송 - **포괄적인 오류 처리**: 각 오류 유형에 대한 특정 예외 클래스 ## 보이스 추천 원하는 스타일은 알지만 정확한 `voice_id`를 모를 때 `recommendVoices`를 사용합니다. ```kotlin val voices = client.recommendVoices( "warm female voice for a product tutorial", count = 3, ) voices.forEach { voice -> println("${voice.voiceId} ${voice.voiceName} ${voice.score}") } ``` 추천 결과에는 `voiceId`, `voiceName`, `score`만 포함됩니다. 지원 모델, 감정, 성별, 연령대, 사용 사례 같은 상세 메타데이터가 필요하면 `getVoiceV2` 또는 `getVoicesV2`로 추가 조회하세요. ## 구성 환경 변수, `.env` 파일 또는 빌더를 통해 API 키를 설정하세요: ```kotlin // 환경 변수 사용 // export TYPECAST_API_KEY="your-api-key-here" val client = TypecastClient.create() // 또는 직접 전달 val client = TypecastClient.create("your-api-key-here") // 또는 사용자 정의 구성을 위해 빌더 사용 val client = TypecastClient.builder() .apiKey("your-api-key-here") .baseUrl("https://custom-api.example.com") .build() ``` 자체 프록시를 통해 요청하는 경우 `baseUrl`을 프록시 엔드포인트로 설정하고 `apiKey`를 생략할 수 있습니다. API 키가 비어 있거나 없으면 SDK는 `X-API-KEY` 헤더를 보내지 않습니다. 기본 Typecast 호스트로 요청할 때는 API 키가 계속 필요합니다. ```kotlin API 키 없는 프록시 val client = TypecastClient.builder() .baseUrl("https://your-proxy.example.com") .build() ``` ### 환경 파일 프로젝트 루트에 `.env` 파일을 만드세요: ```bash TYPECAST_API_KEY=your-api-key-here ``` ## 고급 사용법 ### 감정 제어 (ssfm-v30) ssfm-v30은 두 가지 감정 제어 모드를 제공합니다: **프리셋** 및 **스마트**. AI가 문맥에서 감정을 추론하도록 합니다: ```kotlin val request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("모든 것이 잘 될 거예요.") .model(TTSModel.SSFM_V30) .prompt(SmartPrompt.builder() .previousText("방금 최고의 소식을 들었어요!") // 선택적 문맥 .nextText("축하할 수 있어서 너무 기다려져요!") // 선택적 문맥 .build()) .build() val response = client.textToSpeech(request) ``` 프리셋 값으로 감정을 명시적으로 설정합니다: ```kotlin val request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("이 기능들을 보여드리게 되어 정말 기대됩니다!") .model(TTSModel.SSFM_V30) .prompt(PresetPrompt.builder() .emotionPreset(EmotionPreset.HAPPY) // normal, happy, sad, angry, whisper, toneup, tonedown .emotionIntensity(1.5) // 범위: 0.0 ~ 2.0 .build()) .build() val response = client.textToSpeech(request) ``` ### 오디오 사용자 정의 라우드니스, 피치, 템포 및 출력 형식을 제어합니다: ```kotlin val request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("사용자 정의 오디오 출력!") .model(TTSModel.SSFM_V30) .output(Output.builder() .targetLufs(-14.0) // 범위: -70 ~ 0 (LUFS) .audioPitch(2) // 범위: -12 to +12 반음 .audioTempo(1.2) // 범위: 0.5x to 2.0x .audioFormat(AudioFormat.MP3) // 옵션: WAV, MP3 .build()) .seed(42) // 부호 없는 정수 시드 (재현 가능한 결과) .build() val response = client.textToSpeech(request) File("output.${response.format}").writeBytes(response.audioData) println("Duration: ${response.duration}s, Format: ${response.format}") ``` ### 파일로 바로 생성하기 `generateToFile`은 음성 합성과 파일 저장을 한 번에 처리합니다. `model`은 기본값으로 `ssfm-v30`을 사용하고, `.mp3` 또는 `.wav` 확장자로 출력 형식을 결정합니다. ```kotlin client.generateToFile( "output.mp3", GenerateToFileRequest( text = "안녕하세요, 타입캐스트입니다.", voiceId = "tc_672c5f5ce59fac2a48faeaee", // voice_id는 https://studio.typecast.ai/developers/api/voices 에서 확인하세요. ) ) ``` ### 텍스트만으로 쉼 표현 한 voice로 읽는 문장 안에 쉼만 넣고 싶다면 텍스트에 pause markup을 직접 작성합니다. `<|5s|>`, `<|1s|>`, `<|0.3s|>`, `<|0.34413s|>`처럼 쓰며 값은 초 단위이고 반드시 `s`로 끝납니다. 별도 pause 함수를 호출하지 않아도 텍스트만 보고 쉼 위치를 확인할 수 있습니다. ```kotlin val audio = client.composeSpeech() .defaults(ComposerSettings(voiceId = "tc_672c5f5ce59fac2a48faeaee", model = TTSModel.SSFM_V30)) .say("안녕하세요<|5s|>반갑습니다<|1s|>오늘<|2s|>날씨는 어떤 것 같으세요?") .generate() ``` ### 다중 화자 합성 한 파일 안에서 서로 다른 voice나 구간별 pitch, tempo, prompt, seed 같은 옵션을 조합해야 할 때 사용합니다. composer는 세그먼트를 `POST /v1/text-to-speech/compose`로 보내며 WAV 또는 MP3를 직접 반환합니다. 무음 제거는 TTS 세그먼트에 명시적으로 설정하고, 명시적인 쉼은 유지됩니다. ```kotlin val client = TypecastClient.create("YOUR_API_KEY") val audio = client.composeSpeech() .defaults(ComposerSettings(voiceId = "tc_672c5f5ce59fac2a48faeaee", model = TTSModel.SSFM_V30)) .say("Hello there") .pause(5.0) .say("Nice to meet you", ComposerSettings(voiceId = "tc_60e5426de8b95f1d3000d7b5", output = Output(audioPitch = 2))) .say("Today") .pause(2.0) .say("How does the weather feel?") .generate() File("conversation.wav").writeBytes(audio.audioData) ``` ### 음성 탐색 (V2 API) 향상된 메타데이터로 사용 가능한 음성을 나열하고 필터링합니다: ```kotlin // 모든 음성 가져오기 val voices = client.getVoicesV2() // 기준으로 필터링 val filter = VoicesV2Filter.builder() .model(TTSModel.SSFM_V30) .gender(GenderEnum.FEMALE) .age(AgeEnum.YOUNG_ADULT) .build() val filtered = client.getVoicesV2(filter) // 음성 정보 표시 voices.forEach { voice -> println("ID: ${voice.voiceId}, Name: ${voice.voiceName}") println("Gender: ${voice.gender}, Age: ${voice.age}") voice.models.forEach { model -> println("Model: ${model.version}, Emotions: ${model.emotions}") } voice.useCases?.let { useCases -> println("Use cases: ${useCases.joinToString(", ")}") } } ``` ### 다국어 콘텐츠 SDK는 자동 언어 감지와 함께 35+개 언어를 지원합니다: ```kotlin // 자동 언어 감지 (권장) val request = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("こんにちは。お元気ですか。") .model(TTSModel.SSFM_V30) .build() val response = client.textToSpeech(request) // 또는 언어를 명시적으로 지정 val koreanRequest = TTSRequest.builder() .voiceId("tc_672c5f5ce59fac2a48faeaee") .text("안녕하세요. 반갑습니다.") .model(TTSModel.SSFM_V30) .language(LanguageCode.KOR) // ISO 639-3 언어 코드 .build() val koreanResponse = client.textToSpeech(koreanRequest) File("output.${response.format}").writeBytes(response.audioData) ``` ### 스트리밍 저지연 재생을 위한 실시간 오디오 청크 스트리밍: ```kotlin import javax.sound.sampled.* // 오디오 재생 설정: 32000 Hz, 16비트, 모노, 리틀엔디안 val format = AudioFormat(32000f, 16, 1, true, false) val line = AudioSystem.getSourceDataLine(format).apply { open(format, 8192) start() } val stream = client.textToSpeechStream(request) val buf = ByteArray(4096) var first = true while (true) { val bytesRead = stream.read(buf) if (bytesRead == -1) break var offset = 0 var len = bytesRead if (first) { offset = 44 // 44바이트 WAV 헤더 건너뛰기 len -= 44 first = false } line.write(buf, offset, len) } line.drain() line.close() stream.close() client.close() ``` **WAV 스트리밍 형식:** 32000 Hz, 16비트, 모노 PCM. 첫 번째 청크에 44바이트 WAV 헤더(size = `0xFFFFFFFF`)가 포함되며, 이후 청크는 원시 PCM 데이터만 포함합니다. MP3 형식: 320 kbps, 44100 Hz, 각 청크는 독립적으로 디코딩 가능합니다. ## 타임스탬프 TTS `textToSpeechWithTimestamps()`는 `POST /v1/text-to-speech/with-timestamps`를 래핑하며, 오디오와 함께 단어·문자 단위 정렬 데이터를 반환합니다. 가라오케 하이라이트, 자막 생성, 립싱크 애플리케이션에 활용할 수 있습니다. ### 기본 사용법 ```kotlin import com.neosapience.TypecastClient import com.neosapience.models.TTSRequestWithTimestamps import java.nio.file.Files import java.nio.file.Paths val client = TypecastClient("YOUR_API_KEY") val result = client.textToSpeechWithTimestamps( TTSRequestWithTimestamps.builder() .voiceId("tc_60e5426de8b95f1d3000d7b5") .text("Hello. How are you?") .model("ssfm-v30") .build() ) Files.write(Paths.get("output.wav"), result.audioBytes()) println("재생 시간: ${"%.3f".format(result.audioDuration)}초") result.words.forEach { word -> println(" [${"%.3f".format(word.startTime)}s – ${"%.3f".format(word.endTime)}s] ${word.text}") } ``` ### 정밀도(Granularity) 설정 `granularity("word")`(기본값) 또는 `granularity("char")`을 설정해 정렬 단위를 제어합니다. ```kotlin // 문자 단위 정렬 - 일본어·중국어에 필수 val result = client.textToSpeechWithTimestamps( TTSRequestWithTimestamps.builder() .voiceId("tc_60e5426de8b95f1d3000d7b5") .text("Hello. How are you?") .model("ssfm-v30") .granularity("char") .build() ) ``` ### 자막 내보내기 ```kotlin val srt = result.toSrt() Files.writeString(Paths.get("output.srt"), srt) val vtt = result.toVtt() Files.writeString(Paths.get("output.vtt"), vtt) ``` **일본어·중국어:** 공백 구분자가 없는 언어(jpn, zho)는 단어 단위 세그먼트가 의미를 갖지 않습니다. 해당 언어에는 `granularity("char")`를 사용해 문자 단위 정렬 데이터를 얻으세요. ## 지원 언어 SDK는 자동 언어 감지와 함께 35+개 언어를 지원합니다: | 코드 | 언어 | 코드 | 언어 | 코드 | 언어 | |------|----------|------|----------|------|----------| | `ENG` | 영어 | `JPN` | 일본어 | `UKR` | 우크라이나어 | | `KOR` | 한국어 | `ELL` | 그리스어 | `IND` | 인도네시아어 | | `SPA` | 스페인어 | `TAM` | 타밀어 | `DAN` | 덴마크어 | | `DEU` | 독일어 | `TGL` | 타갈로그어 | `SWE` | 스웨덴어 | | `FRA` | 프랑스어 | `FIN` | 핀란드어 | `MSA` | 말레이어 | | `ITA` | 이탈리아어 | `ZHO` | 중국어 | `CES` | 체코어 | | `POL` | 폴란드어 | `SLK` | 슬로바키아어 | `POR` | 포르투갈어 | | `NLD` | 네덜란드어 | `ARA` | 아랍어 | `BUL` | 불가리아어 | | `RUS` | 러시아어 | `HRV` | 크로아티아어 | `RON` | 루마니아어 | | `BEN` | 벵골어 | `HIN` | 힌디어 | `HUN` | 헝가리어 | | `NAN` | 민난어 | `NOR` | 노르웨이어 | `PAN` | 펀자브어 | | `THA` | 태국어 | `TUR` | 터키어 | `VIE` | 베트남어 | | `YUE` | 광둥어 | | | | | 지정하지 않으면 입력 텍스트에서 언어가 자동으로 감지됩니다. ## 오류 처리 SDK는 API 오류 처리를 위한 특정 예외 클래스를 제공합니다: ```kotlin import com.neosapience.TypecastClient import com.neosapience.exceptions.* try { val response = client.textToSpeech(request) } catch (e: UnauthorizedException) { // 401: 잘못된 API 키 println("Invalid API key: ${e.message}") } catch (e: PaymentRequiredException) { // 402: 크레딧 부족 println("Insufficient credits: ${e.message}") } catch (e: ForbiddenException) { // 403: 접근 거부 println("Access denied: ${e.message}") } catch (e: NotFoundException) { // 404: 리소스를 찾을 수 없음 println("Voice not found: ${e.message}") } catch (e: UnprocessableEntityException) { // 422: 유효성 검사 오류 println("Validation error: ${e.message}") } catch (e: RateLimitException) { // 429: 요청 한도 초과 println("Rate limit exceeded - please try again later") } catch (e: InternalServerException) { // 500: 서버 오류 println("Server error: ${e.message}") } catch (e: TypecastException) { // 일반 오류 println("API error (${e.statusCode}): ${e.message}") } ``` ### 예외 계층 구조 | 예외 | 상태 코드 | 설명 | |-----------|-------------|-------------| | `BadRequestException` | 400 | 잘못된 요청 매개변수 | | `UnauthorizedException` | 401 | 잘못되거나 누락된 API 키 | | `PaymentRequiredException` | 402 | 크레딧 부족 | | `ForbiddenException` | 403 | 접근 거부 | | `NotFoundException` | 404 | 리소스를 찾을 수 없음 | | `UnprocessableEntityException` | 422 | 유효성 검사 오류 | | `RateLimitException` | 429 | 요청 한도 초과 | | `InternalServerException` | 500 | 서버 오류 | | `TypecastException` | * | 기본 예외 클래스 | ## IntelliJ IDEA 설정 1. IntelliJ IDEA 열기 2. `File` → `New` → `Project...` 이동 3. "Kotlin" 및 "Gradle (Kotlin)" 선택 4. JDK를 17 이상으로 설정 `build.gradle.kts`에 추가: ```kotlin dependencies { implementation("com.neosapience:typecast-kotlin:1.2.13") } ``` Gradle 동기화 버튼 클릭 또는 `build.gradle.kts` 우클릭 → `Reload Gradle Project` ## Android 설정 앱의 `build.gradle.kts`에 추가: ```kotlin dependencies { implementation("com.neosapience:typecast-kotlin:1.2.13") } ``` `AndroidManifest.xml`에 추가: ```xml ``` 코루틴 또는 백그라운드 스레드에서 API 호출: ```kotlin lifecycleScope.launch(Dispatchers.IO) { val client = TypecastClient.create("YOUR_API_KEY") val response = client.textToSpeech(request) // 응답 처리 } ``` ## API 레퍼런스 ### TypecastClient 메서드 | 메서드 | 설명 | |--------|-------------| | `textToSpeech(TTSRequest)` | 텍스트를 음성 오디오로 변환 | | `generateToFile(path, GenerateToFileRequest)` | 음성을 생성하고 로컬 파일로 바로 저장 | | `getVoicesV2()` | 모든 사용 가능한 음성 가져오기 | | `getVoicesV2(VoicesV2Filter)` | 필터링된 음성 가져오기 | | `getVoiceV2(voiceId: String)` | ID로 특정 음성 가져오기 | | `close()` | 리소스 해제 | ### TTSRequest 필드 | 필드 | 타입 | 필수 | 설명 | |-------|------|----------|-------------| | `voiceId` | `String` | ✓ | 음성 ID (형식: `tc_*` 또는 `uc_*`) | | `text` | `String` | ✓ | 합성할 텍스트 (최대 2000자) | | `model` | `TTSModel` | ✓ | TTS 모델 (`SSFM_V21` 또는 `SSFM_V30`) | | `language` | `LanguageCode` | | ISO 639-3 코드 (생략 시 자동 감지) | | `prompt` | `Prompt` / `PresetPrompt` / `SmartPrompt` | | 감정 설정 | | `output` | `Output` | | 오디오 출력 설정 | | `seed` | `UInt32` | | 재현성을 위한 부호 없는 정수 시드 (≥ 0) | ### TTSResponse 필드 | 필드 | 타입 | 설명 | |-------|------|-------------| | `audioData` | `ByteArray` | 생성된 오디오 데이터 | | `duration` | `Double` | 오디오 길이(초) | | `format` | `String` | 오디오 형식 (`wav` 또는 `mp3`) | ## 무음 길이 조절 이 기능은 **1.2.13 이상**에서 지원합니다. `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. 일반·스트리밍·타임스탬프 TTS는 `output.remove_silence_ms`, Compose는 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 전달합니다. 반환 타임스탬프는 처리 후 오디오를 기준으로 하며, 명시적인 `pause` 세그먼트는 유지됩니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. 아래 출력 설정을 해당 요청의 `output`에 전달하세요. 스트리밍은 스트리밍 전용 출력 타입을 사용합니다. ```kotlin val output = Output(removeSilenceMs = 300) val streamOutput = OutputStream(removeSilenceMs = 300) ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # C/C++ [타입캐스트 API](https://typecast.ai)용 공식 C/C++ 라이브러리입니다. AI 기반 음성을 사용하여 텍스트를 자연스러운 음성으로 변환하세요. C11 이상 버전과 호환됩니다. CMake, 수동 컴파일을 지원하며 Windows, Linux, macOS 및 임베디드 시스템을 포함한 크로스 플랫폼 개발이 가능합니다. Typecast C SDK 소스 코드 타입캐스트 API 문서 ## 요구 사항 - CMake 3.14+ - libcurl (SSL 지원 필요) - C11 호환 컴파일러 ```bash sudo apt-get install build-essential cmake libcurl4-openssl-dev ``` ```bash # libcurl은 Xcode에 포함되어 있습니다 xcode-select --install brew install cmake ``` ```powershell vcpkg install curl:x64-windows ``` ## 설치 저장소를 복제하고 CMake로 빌드합니다: ```bash git clone https://github.com/neosapience/typecast-sdk.git cd typecast-sdk/typecast-c mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release cmake --build . ``` GCC 또는 Clang으로 직접 컴파일합니다: ```bash git clone https://github.com/neosapience/typecast-sdk.git cd typecast-sdk/typecast-c # 소스 파일 컴파일 gcc -c src/typecast.c src/cJSON.c -I include -I src -O2 # 정적 라이브러리 생성 ar rcs libtypecast.a typecast.o cJSON.o # 또는 애플리케이션 직접 컴파일 gcc -o myapp myapp.c src/typecast.c src/cJSON.c \ -I include -I src -lcurl -O2 ``` `CMakeLists.txt`에 추가합니다: ```cmake include(FetchContent) FetchContent_Declare( typecast GIT_REPOSITORY https://github.com/neosapience/typecast-sdk.git SOURCE_SUBDIR typecast-c GIT_TAG v1.2.13 ) FetchContent_MakeAvailable(typecast) target_link_libraries(your_target PRIVATE typecast) ``` 최신 등록 버전은 SDK Git 태그 기준 **v1.2.13**입니다. ### 빌드 옵션 | 옵션 | 기본값 | 설명 | |--------|---------|-------------| | `TYPECAST_BUILD_SHARED` | ON | 공유 라이브러리 빌드 (.dll/.so/.dylib) | | `TYPECAST_BUILD_STATIC` | OFF | 정적 라이브러리 빌드 | | `TYPECAST_BUILD_EXAMPLES` | ON | 예제 프로그램 빌드 | | `TYPECAST_BUILD_TESTS` | ON | 테스트 프로그램 빌드 | ## 빠른 시작 ```c #include "typecast.h" #include int main() { // 클라이언트 초기화 TypecastClient* client = typecast_client_create("YOUR_API_KEY"); if (!client) return 1; // 텍스트를 음성으로 변환 TypecastTTSRequest request = {0}; request.text = "안녕하세요! 저는 친절한 텍스트-투-스피치 에이전트입니다."; request.voice_id = "tc_672c5f5ce59fac2a48faeaee"; request.model = TYPECAST_MODEL_SSFM_V30; request.language = "kor"; TypecastTTSResponse* response = typecast_text_to_speech(client, &request); if (response) { // 오디오 파일 저장 FILE* fp = fopen("output.wav", "wb"); fwrite(response->audio_data, 1, response->audio_size, fp); fclose(fp); printf("Audio saved! Duration: %.2fs, Size: %zu bytes\n", response->duration, response->audio_size); typecast_tts_response_free(response); } // 정리 typecast_client_destroy(client); return 0; } ``` ## 기능 Typecast C/C++ SDK는 텍스트-투-스피치 변환을 위한 강력한 기능을 제공합니다: - **C 및 C++ 지원**: 순수 C API와 편의를 위한 선택적 C++ 래퍼 제공 - **다중 음성 모델**: `ssfm-v30` (최신) 및 `ssfm-v21` AI 음성 모델 지원 - **다국어 지원**: 영어, 한국어, 스페인어, 일본어, 중국어 등 35+개 언어 지원 - **감정 제어**: 이모션 프리셋 (normal, happy, sad, angry, whisper, toneup, tonedown) 또는 스마트 문맥 인식 추론 - **오디오 커스터마이징**: 라우드니스 (LUFS -70 to 0), 피치 (-12 to +12 반음), 템포 (0.5x to 2.0x), 포맷 (WAV/MP3) 제어 - **음성 검색**: 모델, 성별, 나이, 사용 사례별 필터링이 가능한 V2 Voices API - **크로스 플랫폼**: Windows, Linux, macOS, ARM (32/64비트) 지원 - **타임스탬프 TTS**: 자막, 가라오케, 립싱크를 위한 단어·문자 단위 정렬 데이터 - **스트리밍**: 저지연 재생을 위한 실시간 청크 오디오 전송 - **임베디드 지원**: 최소 용량 최적화, 크로스 컴파일 지원 - **언리얼 엔진 지원**: 게임 엔진과의 쉬운 통합을 위해 설계됨 ## 보이스 추천 원하는 스타일은 알지만 정확한 `voice_id`를 모를 때 `typecast_recommend_voices`를 사용합니다. ```c TypecastRecommendedVoicesResponse* voices = typecast_recommend_voices( client, "warm female voice for a product tutorial", 3 ); if (voices) { for (int i = 0; i < voices->count; i++) { printf("%s %s %.3f\n", voices->voices[i].voice_id, voices->voices[i].voice_name, voices->voices[i].score); } typecast_recommended_voices_response_free(voices); } ``` 추천 결과에는 `voice_id`, `voice_name`, `score`만 포함됩니다. 지원 모델, 감정, 성별, 연령대, 사용 사례 같은 상세 메타데이터가 필요하면 `typecast_get_voice` 또는 `typecast_get_voices`로 추가 조회하세요. ## 설정 환경 변수 또는 생성자를 통해 API 키를 설정합니다: ```c #include // 환경 변수 사용 // export TYPECAST_API_KEY="your-api-key-here" const char* api_key = getenv("TYPECAST_API_KEY"); TypecastClient* client = typecast_client_create(api_key); // 또는 직접 전달 TypecastClient* client = typecast_client_create("your-api-key-here"); // 또는 커스텀 베이스 URL과 함께 TypecastClient* client = typecast_client_create_with_host( "your-api-key-here", "https://custom-api.example.com" ); ``` 자체 프록시를 통해 요청하는 경우 프록시 호스트를 전달하고 API 키는 `NULL` 또는 빈 문자열로 생략할 수 있습니다. API 키가 비어 있거나 없으면 SDK는 `X-API-KEY` 헤더를 보내지 않습니다. 기본 Typecast 호스트로 요청할 때는 API 키가 계속 필요합니다. ```c API 키 없는 프록시 TypecastClient* client = typecast_client_create_with_host( NULL, "https://your-proxy.example.com" ); ``` ## 고급 사용법 ### 감정 제어 (ssfm-v30) ssfm-v30은 두 가지 감정 제어 모드를 제공합니다: **프리셋** 및 **스마트**. AI가 문맥에서 감정을 추론하도록 합니다: ```c TypecastTTSRequest request = {0}; request.text = "모든 것이 잘 될 거예요."; request.voice_id = "tc_672c5f5ce59fac2a48faeaee"; request.model = TYPECAST_MODEL_SSFM_V30; request.language = "kor"; // 문맥을 활용한 스마트 이모션 TypecastPrompt prompt = {0}; prompt.emotion_type = TYPECAST_EMOTION_TYPE_SMART; prompt.previous_text = "방금 최고의 소식을 들었어요!"; // 선택적 문맥 prompt.next_text = "축하하고 싶어서 기다릴 수가 없어요!"; // 선택적 문맥 request.prompt = &prompt; TypecastTTSResponse* response = typecast_text_to_speech(client, &request); ``` 프리셋 값으로 감정을 명시적으로 설정합니다: ```c TypecastTTSRequest request = {0}; request.text = "이 기능들을 보여드리게 되어 정말 기뻐요!"; request.voice_id = "tc_672c5f5ce59fac2a48faeaee"; request.model = TYPECAST_MODEL_SSFM_V30; request.language = "kor"; // 이모션 프리셋 TypecastPrompt prompt = TYPECAST_PROMPT_DEFAULT(); prompt.emotion_type = TYPECAST_EMOTION_TYPE_PRESET; prompt.emotion_preset = TYPECAST_EMOTION_HAPPY; // normal, happy, sad, angry, whisper, toneup, tonedown prompt.emotion_intensity = 1.5f; // 범위: 0.0 ~ 2.0 request.prompt = &prompt; TypecastTTSResponse* response = typecast_text_to_speech(client, &request); ``` ### 오디오 커스터마이징 라우드니스, 피치, 템포 및 출력 포맷을 제어합니다: ```c TypecastTTSRequest request = {0}; request.text = "커스터마이징된 오디오 출력입니다!"; request.voice_id = "tc_672c5f5ce59fac2a48faeaee"; request.model = TYPECAST_MODEL_SSFM_V30; request.language = "kor"; // 출력 설정 구성 TypecastOutput output = TYPECAST_OUTPUT_DEFAULT(); output.use_target_lufs = 1; output.target_lufs = -14.0f; // 범위: -70 ~ 0 (LUFS) output.audio_pitch = 2; // 범위: -12 to +12 반음 output.audio_tempo = 1.2f; // 범위: 0.5x to 2.0x output.audio_format = TYPECAST_AUDIO_FORMAT_MP3; // 옵션: WAV, MP3 request.output = &output; request.seed = 42; // 부호 없는 정수 시드 (재현 가능한 결과) TypecastTTSResponse* response = typecast_text_to_speech(client, &request); if (response) { const char* ext = (response->format == TYPECAST_AUDIO_FORMAT_MP3) ? "mp3" : "wav"; char filename[64]; snprintf(filename, sizeof(filename), "output.%s", ext); FILE* fp = fopen(filename, "wb"); fwrite(response->audio_data, 1, response->audio_size, fp); fclose(fp); printf("Duration: %.2fs, Format: %s\n", response->duration, ext); typecast_tts_response_free(response); } ``` ### 파일로 바로 생성하기 `typecast_generate_to_file`은 음성 합성과 파일 저장을 한 번에 처리합니다. `model`은 기본값으로 `ssfm-v30`을 사용하고, `.mp3` 또는 `.wav` 확장자로 출력 형식을 결정합니다. ```c int result = typecast_generate_to_file( client, "hello.mp3", "안녕하세요, 타입캐스트입니다.", "tc_672c5f5ce59fac2a48faeaee", /* voice_id는 https://studio.typecast.ai/developers/api/voices 에서 확인하세요. */ NULL ); ``` ### 텍스트만으로 쉼 표현 한 voice로 읽는 문장 안에 쉼만 넣고 싶다면 텍스트에 pause markup을 직접 작성합니다. `<|5s|>`, `<|1s|>`, `<|0.3s|>`, `<|0.34413s|>`처럼 쓰며 값은 초 단위이고 반드시 `s`로 끝납니다. 별도 pause 함수를 호출하지 않아도 텍스트만 보고 쉼 위치를 확인할 수 있습니다. ```c TypecastSpeechComposer* composer = typecast_speech_composer_create(client); TypecastComposerSettings defaults = {0}; defaults.voice_id = "tc_672c5f5ce59fac2a48faeaee"; defaults.model = TYPECAST_MODEL_SSFM_V30; typecast_speech_composer_defaults(composer, &defaults); typecast_speech_composer_say( composer, "안녕하세요<|5s|>반갑습니다<|1s|>오늘<|2s|>날씨는 어떤 것 같으세요?", NULL ); TypecastTTSResponse* audio = typecast_speech_composer_generate(composer, TYPECAST_AUDIO_FORMAT_WAV); typecast_tts_response_free(audio); typecast_speech_composer_destroy(composer); ``` ### 다중 화자 합성 한 파일 안에서 서로 다른 voice나 구간별 pitch, tempo, prompt, seed 같은 옵션을 조합해야 할 때 사용합니다. composer는 세그먼트를 `POST /v1/text-to-speech/compose`로 보내며 WAV 또는 MP3를 직접 반환합니다. 무음 제거는 TTS 세그먼트에 명시적으로 설정하고, 명시적인 쉼은 유지됩니다. ```c TypecastSpeechComposer* composer = typecast_speech_composer_create(client); TypecastComposerSettings defaults = {0}; defaults.voice_id = "tc_672c5f5ce59fac2a48faeaee"; defaults.model = TYPECAST_MODEL_SSFM_V30; typecast_speech_composer_defaults(composer, &defaults); typecast_speech_composer_say(composer, "Hello there", NULL); typecast_speech_composer_pause(composer, 5.0f); TypecastComposerSettings second = {0}; second.voice_id = "tc_60e5426de8b95f1d3000d7b5"; second.output.audio_pitch = 2; typecast_speech_composer_say(composer, "Nice to meet you", &second); typecast_speech_composer_pause(composer, 2.0f); typecast_speech_composer_say(composer, "How does the weather feel?", NULL); TypecastTTSResponse* audio = typecast_speech_composer_generate(composer, TYPECAST_AUDIO_FORMAT_WAV); /* write audio->audio_data / audio->audio_len to conversation.wav */ typecast_tts_response_free(audio); typecast_speech_composer_destroy(composer); ``` ### 음성 검색 (V2 API) 향상된 메타데이터로 사용 가능한 음성을 나열하고 필터링합니다: ```c // 모든 음성 가져오기 TypecastVoicesResponse* voices = typecast_get_voices(client, NULL); // 또는 조건으로 필터링 TypecastModel model = TYPECAST_MODEL_SSFM_V30; TypecastGender gender = TYPECAST_GENDER_FEMALE; TypecastAge age = TYPECAST_AGE_YOUNG_ADULT; TypecastVoicesFilter filter = {0}; filter.model = &model; filter.gender = &gender; filter.age = &age; TypecastVoicesResponse* filtered = typecast_get_voices(client, &filter); // 음성 정보 표시 if (voices) { for (size_t i = 0; i < voices->count; i++) { TypecastVoice* v = &voices->voices[i]; printf("ID: %s, Name: %s\n", v->voice_id, v->voice_name); printf("Gender: %d, Age: %d\n", v->gender, v->age); for (size_t j = 0; j < v->models_count; j++) { printf("Model: %s, Emotions: ", typecast_model_to_string(v->models[j].version)); for (size_t k = 0; k < v->models[j].emotions_count; k++) { printf("%s ", v->models[j].emotions[k]); } printf("\n"); } } typecast_voices_response_free(voices); } ``` ### 다국어 콘텐츠 SDK는 자동 언어 감지와 함께 35+개 언어를 지원합니다: ```c // 자동 언어 감지 (권장 - language 필드 생략) TypecastTTSRequest request = {0}; request.text = "こんにちは。お元気ですか。"; request.voice_id = "tc_672c5f5ce59fac2a48faeaee"; request.model = TYPECAST_MODEL_SSFM_V30; // language가 NULL이면 자동으로 감지됩니다 TypecastTTSResponse* response = typecast_text_to_speech(client, &request); // 또는 ISO 639-3 코드로 언어를 명시적으로 지정 TypecastTTSRequest korean_request = {0}; korean_request.text = "안녕하세요. 반갑습니다."; korean_request.voice_id = "tc_672c5f5ce59fac2a48faeaee"; korean_request.model = TYPECAST_MODEL_SSFM_V30; korean_request.language = "kor"; // ISO 639-3 언어 코드 TypecastTTSResponse* korean_response = typecast_text_to_speech(client, &korean_request); ``` ### 스트리밍 저지연 재생을 위한 실시간 오디오 청크 스트리밍: ```c // 실시간 재생을 위한 원시 PCM 추출 (44바이트 WAV 헤더 건너뛰기) static int g_first = 1; static int on_chunk(const uint8_t *data, size_t len, void *user_data) { const uint8_t *pcm = data; size_t pcm_len = len; if (g_first) { pcm += 44; // WAV 헤더 건너뛰기 pcm_len -= 44; g_first = 0; } // pcm은 32000 Hz 16비트 모노 원시 PCM // 오디오 출력으로 전달 (예: PortAudio, ALSA) play_audio(pcm, pcm_len); // 재생 함수 return 0; } ``` **WAV 스트리밍 형식:** 32000 Hz, 16비트, 모노 PCM. 첫 번째 청크에 44바이트 WAV 헤더(size = `0xFFFFFFFF`)가 포함되며, 이후 청크는 원시 PCM 데이터만 포함합니다. MP3 형식: 320 kbps, 44100 Hz, 각 청크는 독립적으로 디코딩 가능합니다. 안전한 기본값으로 출력 설정을 초기화하려면 `TYPECAST_OUTPUT_STREAM_DEFAULT()`를 사용하세요. ## 타임스탬프 TTS `typecast_text_to_speech_with_timestamps()`는 `POST /v1/text-to-speech/with-timestamps`를 래핑하며, 오디오와 함께 단어·문자 단위 정렬 데이터를 반환합니다. 가라오케 하이라이트, 자막 생성, 립싱크 애플리케이션에 활용할 수 있습니다. ### 기본 사용법 ```c #include "typecast.h" #include #include int main(void) { TypecastClient *client = typecast_client_new("YOUR_API_KEY"); TypecastTTSRequestWithTimestamps req = { .voice_id = "tc_60e5426de8b95f1d3000d7b5", .text = "Hello. How are you?", .model = "ssfm-v30", .granularity = TYPECAST_GRANULARITY_WORD, }; TypecastTTSWithTimestampsResponse *result = typecast_text_to_speech_with_timestamps(client, &req); FILE *f = fopen("output.wav", "wb"); fwrite(result->audio_data, 1, result->audio_size, f); fclose(f); printf("재생 시간: %.3f초\n", result->audio_duration); for (size_t i = 0; i < result->words_count; i++) { printf(" [%.3fs – %.3fs] %s\n", result->words[i].start_time, result->words[i].end_time, result->words[i].text); } typecast_tts_with_timestamps_response_free(result); typecast_client_free(client); return 0; } ``` ### 정밀도(Granularity) 설정 `TYPECAST_GRANULARITY_WORD`(기본값) 또는 `TYPECAST_GRANULARITY_CHAR`를 설정해 정렬 단위를 제어합니다. ```c // 문자 단위 정렬 - 일본어·중국어에 필수 TypecastTTSRequestWithTimestamps req = { .voice_id = "tc_60e5426de8b95f1d3000d7b5", .text = "Hello. How are you?", .model = "ssfm-v30", .granularity = TYPECAST_GRANULARITY_CHAR, }; ``` ### 자막 내보내기 ```c char *srt = typecast_tts_with_timestamps_to_srt(result); FILE *fs = fopen("output.srt", "w"); fputs(srt, fs); fclose(fs); free(srt); char *vtt = typecast_tts_with_timestamps_to_vtt(result); FILE *fv = fopen("output.vtt", "w"); fputs(vtt, fv); fclose(fv); free(vtt); ``` **일본어·중국어:** 공백 구분자가 없는 언어(jpn, zho)는 단어 단위 세그먼트가 의미를 갖지 않습니다. 해당 언어에는 `TYPECAST_GRANULARITY_CHAR`를 사용해 문자 단위 정렬 데이터를 얻으세요. ## 지원 언어 SDK는 자동 언어 감지와 함께 35+개 언어를 지원합니다: | 코드 | 언어 | 코드 | 언어 | 코드 | 언어 | |------|----------|------|----------|------|----------| | `eng` | 영어 | `jpn` | 일본어 | `ukr` | 우크라이나어 | | `kor` | 한국어 | `ell` | 그리스어 | `ind` | 인도네시아어 | | `spa` | 스페인어 | `tam` | 타밀어 | `dan` | 덴마크어 | | `deu` | 독일어 | `tgl` | 타갈로그어 | `swe` | 스웨덴어 | | `fra` | 프랑스어 | `fin` | 핀란드어 | `msa` | 말레이어 | | `ita` | 이탈리아어 | `zho` | 중국어 | `ces` | 체코어 | | `pol` | 폴란드어 | `slk` | 슬로바키아어 | `por` | 포르투갈어 | | `nld` | 네덜란드어 | `ara` | 아랍어 | `bul` | 불가리아어 | | `rus` | 러시아어 | `hrv` | 크로아티아어 | `ron` | 루마니아어 | | `ben` | 벵골어 | `hin` | 힌디어 | `hun` | 헝가리어 | | `nan` | 민난어 | `nor` | 노르웨이어 | `pan` | 펀자브어 | | `tha` | 태국어 | `tur` | 터키어 | `vie` | 베트남어 | | `yue` | 광동어 | | | | | 지정하지 않으면 입력 텍스트에서 언어가 자동으로 감지됩니다. ## 오류 처리 SDK는 API 오류 처리를 위한 특정 오류 코드를 제공합니다: ```c #include "typecast.h" TypecastTTSResponse* response = typecast_text_to_speech(client, &request); if (!response) { const TypecastError* err = typecast_client_get_error(client); switch (err->code) { case TYPECAST_ERROR_UNAUTHORIZED: // 401: 유효하지 않은 API 키 fprintf(stderr, "Invalid API key: %s\n", err->message); break; case TYPECAST_ERROR_PAYMENT_REQUIRED: // 402: 크레딧 부족 fprintf(stderr, "Insufficient credits: %s\n", err->message); break; case TYPECAST_ERROR_NOT_FOUND: // 404: 리소스를 찾을 수 없음 fprintf(stderr, "Voice not found: %s\n", err->message); break; case TYPECAST_ERROR_UNPROCESSABLE_ENTITY: // 422: 유효성 검사 오류 fprintf(stderr, "Validation error: %s\n", err->message); break; case TYPECAST_ERROR_RATE_LIMIT: // 429: 요청 제한 초과 fprintf(stderr, "Rate limit exceeded - please try again later\n"); break; case TYPECAST_ERROR_INTERNAL_SERVER: // 500: 서버 오류 fprintf(stderr, "Server error: %s\n", err->message); break; default: fprintf(stderr, "API error (%d): %s\n", err->code, err->message); break; } } ``` ### 오류 코드 | 오류 코드 | 값 | 설명 | |-----------|-------|-------------| | `TYPECAST_OK` | 0 | 성공 | | `TYPECAST_ERROR_INVALID_PARAM` | -1 | 유효하지 않은 요청 매개변수 | | `TYPECAST_ERROR_OUT_OF_MEMORY` | -2 | 메모리 할당 실패 | | `TYPECAST_ERROR_CURL_INIT` | -3 | libcurl 초기화 실패 | | `TYPECAST_ERROR_NETWORK` | -4 | 네트워크 오류 | | `TYPECAST_ERROR_JSON_PARSE` | -5 | JSON 파싱 오류 | | `TYPECAST_ERROR_BAD_REQUEST` | 400 | 잘못된 요청 | | `TYPECAST_ERROR_UNAUTHORIZED` | 401 | 유효하지 않거나 누락된 API 키 | | `TYPECAST_ERROR_PAYMENT_REQUIRED` | 402 | 크레딧 부족 | | `TYPECAST_ERROR_NOT_FOUND` | 404 | 리소스를 찾을 수 없음 | | `TYPECAST_ERROR_UNPROCESSABLE_ENTITY` | 422 | 유효성 검사 오류 | | `TYPECAST_ERROR_RATE_LIMIT` | 429 | 요청 제한 초과 | | `TYPECAST_ERROR_INTERNAL_SERVER` | 500 | 서버 오류 | ## C++ 래퍼 C++ 프로젝트의 경우, 더 관용적인 인터페이스를 위한 선택적 C++ 래퍼를 활성화할 수 있습니다: ```cpp #define TYPECAST_CPP_WRAPPER #include "typecast.h" #include #include int main() { try { // 클라이언트 초기화 typecast::Client client("YOUR_API_KEY"); // 텍스트를 음성으로 변환 typecast::TTSRequest request; request.text = "안녕하세요! 저는 친절한 텍스트-투-스피치 에이전트입니다."; request.voiceId = "tc_672c5f5ce59fac2a48faeaee"; request.model = typecast::Model::SSFM_V30; request.language = "kor"; auto response = client.textToSpeech(request); // 오디오 파일 저장 std::ofstream file("output.wav", std::ios::binary); file.write(reinterpret_cast(response.audioData.data()), response.audioData.size()); std::cout << "Audio saved! Duration: " << response.duration << "s\n"; } catch (const typecast::TypecastException& e) { std::cerr << "Error (" << e.code << "): " << e.what() << "\n"; return 1; } return 0; } ``` ## 플랫폼 지원 SDK는 다음 플랫폼에서 자동화된 E2E 테스트를 통해 검증되었습니다: | 플랫폼 | 아키텍처 | glibc | C 표준 | 상태 | |----------|--------------|-------|------------|--------| | **CentOS 6.9** | x86_64 | 2.12 | C99 | 검증됨 | | **CentOS 7** | x86_64 | 2.17 | C11 | 검증됨 | | **Amazon Linux 2** | x86_64 | 2.26 | C11 | 검증됨 | | **Ubuntu 20.04 LTS** | x86_64 | 2.31 | C11 | 검증됨 | | **Debian Bullseye** | x86_64 | 2.31 | C11 | 검증됨 | | **Windows** | x64 | N/A | C11 | 검증됨 | | **macOS** | x86_64 / arm64 | N/A | C11 | 검증됨 | ## 임베디드 시스템 이 SDK는 네트워크 연결이 가능한 임베디드 시스템에 통합할 수 있습니다. ### 크로스 컴파일 ```bash mkdir build-arm && cd build-arm cmake .. \ -DCMAKE_TOOLCHAIN_FILE=../cmake/arm-linux-gnueabihf.cmake \ -DTYPECAST_BUILD_STATIC=ON \ -DTYPECAST_BUILD_SHARED=OFF \ -DCMAKE_BUILD_TYPE=MinSizeRel cmake --build . ``` ```bash mkdir build-arm64 && cd build-arm64 cmake .. \ -DCMAKE_SYSTEM_NAME=Linux \ -DCMAKE_SYSTEM_PROCESSOR=aarch64 \ -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc \ -DTYPECAST_BUILD_STATIC=ON \ -DTYPECAST_BUILD_SHARED=OFF \ -DCMAKE_BUILD_TYPE=MinSizeRel cmake --build . ``` ### 메모리 요구 사항 | 구성 요소 | 대략적인 크기 | |-----------|------------------| | 정적 라이브러리 (MinSizeRel) | ~50 KB | | 클라이언트당 런타임 힙 | ~8 KB | | TTS 응답 버퍼 | 가변 (오디오 크기) | | JSON 파싱 버퍼 | ~4 KB | ## 언리얼 엔진 통합 이 SDK는 Unreal Engine 4.27+ 및 Unreal Engine 5.x와의 원활한 통합을 위해 설계되었습니다. 정적 라이브러리로 빌드합니다: ```bash mkdir build && cd build cmake .. -DTYPECAST_BUILD_STATIC=ON -DTYPECAST_BUILD_SHARED=OFF -DCMAKE_BUILD_TYPE=Release cmake --build . --config Release ``` 언리얼 프로젝트에 플러그인을 생성합니다: ``` Plugins/ └── TypecastTTS/ ├── Source/TypecastTTS/ │ ├── Private/ │ ├── Public/ │ └── ThirdParty/Typecast/ │ ├── include/typecast.h │ └── lib/Win64/typecast_static.lib ├── TypecastTTS.uplugin └── TypecastTTS.Build.cs ``` `Build.cs`에 라이브러리 링킹을 추가합니다: ```csharp // 인클루드 경로 추가 PublicIncludePaths.Add(Path.Combine(ThirdPartyPath, "include")); PublicDefinitions.Add("TYPECAST_STATIC"); // 정적 라이브러리 링크 (플랫폼별) if (Target.Platform == UnrealTargetPlatform.Win64) { PublicAdditionalLibraries.Add( Path.Combine(LibPath, "Win64", "typecast_static.lib")); AddEngineThirdPartyPrivateStaticDependencies(Target, "libcurl"); } ``` 블루프린트 지원 및 오디오 재생을 포함한 전체 언리얼 엔진 통합 가이드는 SDK 저장소의 [README](https://github.com/neosapience/typecast-sdk/tree/main/typecast-c)를 참조하세요. ## API 레퍼런스 ### 클라이언트 함수 | 함수 | 설명 | |----------|-------------| | `typecast_client_create(api_key)` | API 키로 클라이언트 생성 | | `typecast_client_create_with_host(api_key, host)` | 커스텀 호스트로 클라이언트 생성 | | `typecast_client_destroy(client)` | 클라이언트 삭제 및 리소스 해제 | | `typecast_client_get_error(client)` | 마지막 오류 정보 가져오기 | ### 텍스트-투-스피치 함수 | 함수 | 설명 | |----------|-------------| | `typecast_text_to_speech(client, request)` | 텍스트를 음성 오디오로 변환 | | `typecast_generate_to_file(client, path, request)` | 음성을 생성하고 로컬 파일로 바로 저장 | | `typecast_tts_response_free(response)` | TTS 응답 메모리 해제 | ### 음성 함수 | 함수 | 설명 | |----------|-------------| | `typecast_get_voices(client, filter)` | 사용 가능한 음성 가져오기 (선택적 필터링) | | `typecast_get_voice(client, voice_id)` | ID로 특정 음성 가져오기 | | `typecast_voices_response_free(response)` | 음성 응답 메모리 해제 | | `typecast_voice_free(voice)` | 단일 음성 메모리 해제 | ### 유틸리티 함수 | 함수 | 설명 | |----------|-------------| | `typecast_version()` | 라이브러리 버전 문자열 가져오기 | | `typecast_model_to_string(model)` | 모델 열거형을 문자열로 변환 | | `typecast_emotion_to_string(emotion)` | 감정 열거형을 문자열로 변환 | | `typecast_audio_format_to_string(format)` | 포맷 열거형을 문자열로 변환 | | `typecast_error_message(code)` | 오류 코드에 대한 오류 메시지 가져오기 | ### TypecastTTSRequest 필드 | 필드 | 타입 | 필수 | 설명 | |-------|------|----------|-------------| | `text` | `const char*` | ✓ | 합성할 텍스트 (최대 2000자) | | `voice_id` | `const char*` | ✓ | 음성 ID (형식: `tc_*` 또는 `uc_*`) | | `model` | `TypecastModel` | ✓ | TTS 모델 (`SSFM_V21` 또는 `SSFM_V30`) | | `language` | `const char*` | | ISO 639-3 코드 (NULL이면 자동 감지) | | `prompt` | `TypecastPrompt*` | | 감정 설정 | | `output` | `TypecastOutput*` | | 오디오 출력 설정 | | `seed` | `unsigned int` | | 재현성을 위한 부호 없는 정수 시드 (≥ 0) | ### TypecastTTSResponse 필드 | 필드 | 타입 | 설명 | |-------|------|-------------| | `audio_data` | `uint8_t*` | 생성된 오디오 데이터 | | `audio_size` | `size_t` | 오디오 데이터 크기 (바이트) | | `duration` | `float` | 오디오 길이 (초) | | `format` | `TypecastAudioFormat` | 오디오 포맷 (wav 또는 mp3) | ## 무음 길이 조절 이 기능은 **1.2.13 이상**에서 지원합니다. `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. 일반·스트리밍·타임스탬프 TTS는 `output.remove_silence_ms`, Compose는 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 전달합니다. 반환 타임스탬프는 처리 후 오디오를 기준으로 하며, 명시적인 `pause` 세그먼트는 유지됩니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. C는 기존 공개 구조체의 ABI를 유지합니다. `client`와 `TypecastTTSRequest request`를 준비한 뒤 확장 함수를 사용하세요. 기존 구조체에 필드를 추가하지 마세요. `NULL`은 비활성화, 정수 포인터는 `0`을 포함한 지정값을 전달합니다. ```c int remaining_silence_ms = 300; TypecastTTSResponse* audio = typecast_text_to_speech_with_silence( client, &request, &remaining_silence_ms ); if (audio != NULL) typecast_tts_response_free(audio); ``` 스트리밍은 `typecast_text_to_speech_stream_with_silence`, 타임스탬프는 `typecast_text_to_speech_with_timestamps_and_silence`, 파일 저장은 `typecast_generate_to_file_with_silence`를 사용합니다. Composer 기본값과 구간별 값은 각각 `typecast_speech_composer_defaults_with_silence`, `typecast_speech_composer_say_with_silence`로 설정합니다. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Swift [타입캐스트 API](https://typecast.ai)를 위한 공식 Swift 라이브러리입니다. AI 기반 음성을 사용하여 텍스트를 생동감 있는 음성으로 변환하세요. Swift 5.9 이상과 호환되며 모든 Apple 플랫폼을 지원합니다: iOS, macOS, tvOS, watchOS, visionOS. Typecast Swift SDK Typecast Swift SDK 소스 코드 ## 요구 사항 | 플랫폼 | 최소 버전 | |----------|-----------------| | iOS | 13.0+ | | macOS | 10.15+ | | tvOS | 13.0+ | | watchOS | 6.0+ | | visionOS | 1.0+ | | Swift | 5.9+ | ## 설치 태그가 지정된 릴리스를 클론한 후 Swift 패키지 디렉터리를 로컬 의존성으로 참조하세요: ```bash git clone --branch typecast-swift/v0.3.14 --depth 1 https://github.com/neosapience/typecast-sdk.git ``` ```swift dependencies: [ .package(path: "typecast-sdk/typecast-swift") ], targets: [ .target( name: "YourTarget", dependencies: [ .product(name: "Typecast", package: "typecast-swift") ] ) ] ``` Xcode에서는 **File** → **Add Package Dependencies...** → **Add Local...**을 선택한 후 클론한 `typecast-swift` 디렉터리를 지정하세요. 최신 등록 버전은 SDK Git 태그 기준 **typecast-swift/v0.3.14**입니다. **Swift 5.9 이상**이 설치되어 있는지 확인하세요. SDK는 이 최소 버전이 필요한 Swift Concurrency(async/await)를 사용합니다. ## 빠른 시작 ```swift import AVFoundation import Typecast let client = TypecastClient(apiKey: "YOUR_API_KEY") var audioPlayer: AVAudioPlayer? // 편의 메서드로 간단하게 사용 let audio = try await client.speak( "안녕하세요! 저는 텍스트 음성 변환 에이전트입니다.", voiceId: "tc_672c5f5ce59fac2a48faeaee" ) // 데이터에서 직접 오디오 재생 audioPlayer = try AVAudioPlayer(data: audio.audioData) audioPlayer?.play() print("Duration: \(audio.duration)s, Format: \(audio.format.rawValue)") ``` ## 기능 Typecast Swift SDK는 텍스트 음성 변환을 위한 강력한 기능을 제공합니다: - **다중 음성 모델**: `ssfm-v30`(최신) 및 `ssfm-v21` AI 음성 모델 지원 - **다국어 지원**: 영어, 한국어, 스페인어, 일본어, 중국어 등 35+개 언어 지원 - **감정 제어**: 이모션 프리셋(normal, happy, sad, angry, whisper, toneup, tonedown) 또는 스마트 문맥 인식 추론 - **오디오 사용자 정의**: 라우드니스 (LUFS -70 to 0), 피치(-12 to +12 반음), 템포(0.5x to 2.0x), 형식(WAV/MP3) 제어 - **음성 탐색**: 모델, 성별, 나이, 사용 사례별 필터링이 가능한 V2 Voices API - **Swift Concurrency**: 현대적인 Swift 개발을 위한 완전한 async/await 지원 - **스레드 안전**: 안전한 동시 사용을 위해 모든 타입이 `Sendable`을 준수 - **타임스탬프 TTS**: 자막, 가라오케, 립싱크를 위한 단어·문자 단위 정렬 데이터 - **스트리밍**: 저지연 재생을 위한 실시간 청크 오디오 전송 - **크로스 플랫폼**: iOS, macOS, tvOS, watchOS, visionOS에서 작동 ## 보이스 추천 원하는 스타일은 알지만 정확한 `voice_id`를 모를 때 `recommendVoices`를 사용합니다. ```swift let voices = try await client.recommendVoices( query: "warm female voice for a product tutorial", count: 3 ) for voice in voices { print("\(voice.voiceId) \(voice.voiceName) \(voice.score)") } ``` 추천 결과에는 `voiceId`, `voiceName`, `score`만 포함됩니다. 지원 모델, 감정, 성별, 연령대, 사용 사례 같은 상세 메타데이터가 필요하면 `getVoice(voiceId:)` 또는 `getVoices(filter:)`로 추가 조회하세요. ## 구성 API 키로 클라이언트를 초기화하세요: ```swift import Typecast // 직접 초기화 let client = TypecastClient(apiKey: "your-api-key") // 사용자 정의 base URL과 함께 let client = TypecastClient( apiKey: "your-api-key", baseURL: "https://api.typecast.ai" ) // 구성 구조체 사용 let config = TypecastConfiguration(apiKey: "your-api-key") let client = TypecastClient(configuration: config) ``` 자체 프록시를 통해 요청하는 경우 `baseURL`을 프록시 엔드포인트로 설정하고 `apiKey`를 생략할 수 있습니다. API 키가 nil이거나 비어 있으면 SDK는 `X-API-KEY` 헤더를 보내지 않습니다. 기본 Typecast 호스트로 요청할 때는 API 키가 계속 필요합니다. ```swift API 키 없는 프록시 let client = TypecastClient( baseURL: "https://your-proxy.example.com" ) ``` ## 고급 사용법 ### 감정 제어 (ssfm-v30) ssfm-v30은 두 가지 감정 제어 모드를 제공합니다: **프리셋** 및 **스마트**. AI가 문맥에서 감정을 추론하도록 합니다: ```swift let request = TTSRequest( voiceId: "tc_672c5f5ce59fac2a48faeaee", text: "모든 것이 잘 될 거예요.", model: .ssfmV30, prompt: .smart(SmartPrompt( previousText: "방금 최고의 소식을 들었어요!", // 선택적 문맥 nextText: "축하할 수 있어서 너무 기다려져요!" // 선택적 문맥 )) ) let response = try await client.textToSpeech(request) audioPlayer = try AVAudioPlayer(data: response.audioData) audioPlayer?.play() ``` 프리셋 값으로 감정을 명시적으로 설정합니다: ```swift let request = TTSRequest( voiceId: "tc_672c5f5ce59fac2a48faeaee", text: "이 기능들을 보여드리게 되어 정말 기대됩니다!", model: .ssfmV30, prompt: .preset(PresetPrompt( emotionPreset: .happy, // normal, happy, sad, angry, whisper, toneup, tonedown emotionIntensity: 1.5 // 범위: 0.0 ~ 2.0 )) ) let response = try await client.textToSpeech(request) audioPlayer = try AVAudioPlayer(data: response.audioData) audioPlayer?.play() ``` 빠른 감정 제어를 위한 편의 메서드를 사용하세요: ```swift let audio = try await client.speak( "정말 기대돼요!", voiceId: "tc_672c5f5ce59fac2a48faeaee", emotion: .happy, intensity: 1.5 ) audioPlayer = try AVAudioPlayer(data: audio.audioData) audioPlayer?.play() ``` ### 오디오 사용자 정의 라우드니스, 피치, 템포 및 출력 형식을 제어합니다: ```swift let request = TTSRequest( voiceId: "tc_672c5f5ce59fac2a48faeaee", text: "사용자 정의 오디오 출력!", model: .ssfmV30, output: OutputSettings( targetLufs: -14.0, // 범위: -70 ~ 0 (LUFS) audioPitch: 2, // 범위: -12 to +12 반음 audioTempo: 1.2, // 범위: 0.5x to 2.0x audioFormat: .mp3 // 옵션: .wav, .mp3 ), seed: 42 // 부호 없는 정수 시드 (재현 가능한 결과) ) let response = try await client.textToSpeech(request) audioPlayer = try AVAudioPlayer(data: response.audioData) audioPlayer?.play() print("Duration: \(response.duration)s, Format: \(response.format.rawValue)") ``` ### 파일로 바로 생성하기 `generateToFile`은 음성 합성과 파일 저장을 한 번에 처리합니다. `model`은 기본값으로 `ssfm-v30`을 사용하고, `.mp3` 또는 `.wav` 확장자로 출력 형식을 결정합니다. ```swift try await client.generateToFile( "output.mp3", request: GenerateToFileRequest( voiceId: "tc_672c5f5ce59fac2a48faeaee", // voice_id는 https://studio.typecast.ai/developers/api/voices 에서 확인하세요. text: "안녕하세요, 타입캐스트입니다." ) ) ``` ### 텍스트만으로 쉼 표현 한 voice로 읽는 문장 안에 쉼만 넣고 싶다면 텍스트에 pause markup을 직접 작성합니다. `<|5s|>`, `<|1s|>`, `<|0.3s|>`, `<|0.34413s|>`처럼 쓰며 값은 초 단위이고 반드시 `s`로 끝납니다. 별도 pause 함수를 호출하지 않아도 텍스트만 보고 쉼 위치를 확인할 수 있습니다. ```swift let audio = try await client.composeSpeech() .defaults(ComposerSettings(voiceId: "tc_672c5f5ce59fac2a48faeaee", model: .ssfmV30)) .say("안녕하세요<|5s|>반갑습니다<|1s|>오늘<|2s|>날씨는 어떤 것 같으세요?") .generate() ``` ### 다중 화자 합성 한 파일 안에서 서로 다른 voice나 구간별 pitch, tempo, prompt, seed 같은 옵션을 조합해야 할 때 사용합니다. composer는 세그먼트를 `POST /v1/text-to-speech/compose`로 보내며 WAV 또는 MP3를 직접 반환합니다. 무음 제거는 TTS 세그먼트에 명시적으로 설정하고, 명시적인 쉼은 유지됩니다. ```swift let audio = try await client.composeSpeech() .defaults(ComposerSettings(voiceId: "tc_672c5f5ce59fac2a48faeaee", model: .ssfmV30)) .say("Hello there") .pause(5) .say("Nice to meet you", overrides: ComposerSettings( voiceId: "tc_60e5426de8b95f1d3000d7b5", output: OutputSettings(audioPitch: 2) )) .say("Today") .pause(2) .say("How does the weather feel?") .generate() try audio.audioData.write(to: URL(fileURLWithPath: "conversation.wav")) ``` ### 음성 탐색 (V2 API) 향상된 메타데이터로 사용 가능한 음성을 나열하고 필터링합니다: ```swift // 모든 음성 가져오기 let voices = try await client.getVoices() // 기준으로 필터링 let filteredVoices = try await client.getVoices(filter: VoicesV2Filter( model: .ssfmV30, gender: .female, age: .youngAdult )) // 특정 음성 가져오기 let voice = try await client.getVoice(voiceId: "tc_672c5f5ce59fac2a48faeaee") // 음성 정보 표시 print("ID: \(voice.voiceId), Name: \(voice.voiceName)") print("Gender: \(voice.gender?.rawValue ?? "N/A"), Age: \(voice.age?.rawValue ?? "N/A")") for model in voice.models { print("Model: \(model.version.rawValue), Emotions: \(model.emotions.joined(separator: ", "))") } if let useCases = voice.useCases { print("Use cases: \(useCases.joined(separator: ", "))") } ``` ### 다국어 콘텐츠 SDK는 자동 언어 감지와 함께 35+개 언어를 지원합니다: ```swift // 자동 언어 감지 (권장) let request = TTSRequest( voiceId: "tc_672c5f5ce59fac2a48faeaee", text: "こんにちは。お元気ですか。", model: .ssfmV30 ) let response = try await client.textToSpeech(request) // 또는 언어를 명시적으로 지정 let koreanRequest = TTSRequest( voiceId: "tc_672c5f5ce59fac2a48faeaee", text: "안녕하세요. 반갑습니다.", model: .ssfmV30, language: .korean // 명시적 언어 코드 ) let koreanResponse = try await client.textToSpeech(koreanRequest) audioPlayer = try AVAudioPlayer(data: koreanResponse.audioData) audioPlayer?.play() ``` ### 스트리밍 저지연 재생을 위한 실시간 오디오 청크 스트리밍: ```swift import AVFoundation import Typecast let engine = AVAudioEngine() let playerNode = AVAudioPlayerNode() let format = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: 32000, channels: 1, interleaved: true)! engine.attach(playerNode) engine.connect(playerNode, to: engine.mainMixerNode, format: format) try engine.start() playerNode.play() let stream = try await client.textToSpeechStream(request) var first = true for try await chunk in stream { var pcmData = chunk if first { pcmData = chunk.dropFirst(44) // 44바이트 WAV 헤더 건너뛰기 first = false } let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: AVAudioFrameCount(pcmData.count / 2))! buffer.frameLength = buffer.frameCapacity pcmData.withUnsafeBytes { ptr in buffer.int16ChannelData!.pointee.update(from: ptr.bindMemory(to: Int16.self).baseAddress!, count: Int(buffer.frameLength)) } playerNode.scheduleBuffer(buffer) } ``` **WAV 스트리밍 형식:** 32000 Hz, 16비트, 모노 PCM. 첫 번째 청크에 44바이트 WAV 헤더(size = `0xFFFFFFFF`)가 포함되며, 이후 청크는 원시 PCM 데이터만 포함합니다. MP3 형식: 320 kbps, 44100 Hz, 각 청크는 독립적으로 디코딩 가능합니다. `Foundation.OutputStream`과의 이름 충돌을 피하려면 `Typecast.OutputStream`을 사용하세요. ## 타임스탬프 TTS `textToSpeechWithTimestamps()`는 `POST /v1/text-to-speech/with-timestamps`를 래핑하며, 오디오와 함께 단어·문자 단위 정렬 데이터를 반환합니다. 가라오케 하이라이트, 자막 생성, 립싱크 애플리케이션에 활용할 수 있습니다. ### 기본 사용법 ```swift import Typecast let client = TypecastClient(apiKey: "YOUR_API_KEY") let result = try await client.textToSpeechWithTimestamps(TTSRequestWithTimestamps( voiceId: "tc_60e5426de8b95f1d3000d7b5", text: "Hello. How are you?", model: "ssfm-v30" )) audioPlayer = try AVAudioPlayer(data: result.audioBytes()) audioPlayer?.play() print(String(format: "재생 시간: %.3f초", result.audioDuration)) for word in result.words { print(String(format: " [%.3fs – %.3fs] %@", word.startTime, word.endTime, word.text)) } ``` ### 정밀도(Granularity) 설정 `granularity: .word`(기본값) 또는 `granularity: .char`를 설정해 정렬 단위를 제어합니다. ```swift // 문자 단위 정렬 - 일본어·중국어에 필수 let result = try await client.textToSpeechWithTimestamps(TTSRequestWithTimestamps( voiceId: "tc_60e5426de8b95f1d3000d7b5", text: "Hello. How are you?", model: "ssfm-v30", granularity: .char )) ``` ### 자막 내보내기 ```swift let srt = try result.toSrt() print(srt) let vtt = try result.toVtt() print(vtt) ``` **일본어·중국어:** 공백 구분자가 없는 언어(jpn, zho)는 단어 단위 세그먼트가 의미를 갖지 않습니다. 해당 언어에는 `.char` 정밀도를 사용해 문자 단위 정렬 데이터를 얻으세요. ## 지원 언어 SDK는 자동 언어 감지와 함께 35+개 언어를 지원합니다: | 코드 | 언어 | 코드 | 언어 | 코드 | 언어 | |------|----------|------|----------|------|----------| | `.english` | 영어 | `.japanese` | 일본어 | `.ukrainian` | 우크라이나어 | | `.korean` | 한국어 | `.greek` | 그리스어 | `.indonesian` | 인도네시아어 | | `.spanish` | 스페인어 | `.tamil` | 타밀어 | `.danish` | 덴마크어 | | `.german` | 독일어 | `.tagalog` | 타갈로그어 | `.swedish` | 스웨덴어 | | `.french` | 프랑스어 | `.finnish` | 핀란드어 | `.malay` | 말레이어 | | `.italian` | 이탈리아어 | `.chinese` | 중국어 | `.czech` | 체코어 | | `.polish` | 폴란드어 | `.slovak` | 슬로바키아어 | `.portuguese` | 포르투갈어 | | `.dutch` | 네덜란드어 | `.arabic` | 아랍어 | `.bulgarian` | 불가리아어 | | `.russian` | 러시아어 | `.croatian` | 크로아티아어 | `.romanian` | 루마니아어 | | `.bengali` | 벵골어 | `.hindi` | 힌디어 | `.hungarian` | 헝가리어 | | `.minNan` | 민난어 | `.norwegian` | 노르웨이어 | `.punjabi` | 펀자브어 | | `.thai` | 태국어 | `.turkish` | 터키어 | `.vietnamese` | 베트남어 | | `.cantonese` | 광둥어 | | | | | 지정하지 않으면 입력 텍스트에서 언어가 자동으로 감지됩니다. ## 오류 처리 SDK는 API 오류 처리를 위한 포괄적인 `TypecastError` 열거형을 제공합니다: ```swift import Typecast do { let response = try await client.textToSpeech(request) } catch let error as TypecastError { switch error { case .unauthorized(let message): // 401: 잘못된 API 키 print("Invalid API key: \(message)") case .paymentRequired(let message): // 402: 크레딧 부족 print("Insufficient credits: \(message)") case .notFound(let message): // 404: 리소스를 찾을 수 없음 print("Voice not found: \(message)") case .validationError(let message): // 422: 유효성 검사 오류 print("Validation error: \(message)") case .rateLimitExceeded(let message): // 429: 요청 한도 초과 print("Rate limit exceeded: \(message)") case .serverError(let message): // 500: 서버 오류 print("Server error: \(message)") case .networkError(let underlyingError): // 네트워크 연결 문제 print("Network error: \(underlyingError.localizedDescription)") case .invalidResponse(let message): // 서버의 잘못된 응답 print("Invalid response: \(message)") default: print("Error: \(error.localizedDescription)") } // 사용 가능한 경우 상태 코드에 액세스 if let statusCode = error.statusCode { print("HTTP status: \(statusCode)") } } ``` ### 오류 타입 | 오류 | 상태 코드 | 설명 | |-------|-------------|-------------| | `.badRequest` | 400 | 잘못된 요청 매개변수 | | `.unauthorized` | 401 | 잘못되거나 누락된 API 키 | | `.paymentRequired` | 402 | 크레딧 부족 | | `.notFound` | 404 | 리소스를 찾을 수 없음 | | `.validationError` | 422 | 유효성 검사 오류 | | `.rateLimitExceeded` | 429 | 요청 한도 초과 | | `.serverError` | 500 | 서버 오류 | | `.networkError` | - | 네트워크 연결 문제 | | `.invalidResponse` | - | 서버의 잘못된 응답 | ## 플랫폼별 사용법 ### iOS ```swift import Typecast import AVFoundation class TTSManager { private let client = TypecastClient(apiKey: "YOUR_API_KEY") private var audioPlayer: AVAudioPlayer? func speak(_ text: String) async throws { let audio = try await client.speak(text, voiceId: "tc_672c5f5ce59fac2a48faeaee") // 데이터에서 직접 오디오 재생 audioPlayer = try AVAudioPlayer(data: audio.audioData) audioPlayer?.play() } } ``` ### macOS ```swift import Typecast import AppKit import AVFoundation class MacTTSManager { private let client = TypecastClient(apiKey: "YOUR_API_KEY") private var audioPlayer: AVAudioPlayer? func speak(_ text: String) async throws { let audio = try await client.speak(text, voiceId: "tc_672c5f5ce59fac2a48faeaee") audioPlayer = try AVAudioPlayer(data: audio.audioData) audioPlayer?.play() } } ``` ### watchOS ```swift import Typecast import AVFoundation class WatchTTSManager { private let client = TypecastClient(apiKey: "YOUR_API_KEY") private var audioPlayer: AVAudioPlayer? func speak(_ text: String) async throws { let audio = try await client.speak(text, voiceId: "tc_672c5f5ce59fac2a48faeaee") audioPlayer = try AVAudioPlayer(data: audio.audioData) audioPlayer?.play() } } ``` ## API 레퍼런스 ### TypecastClient 메서드 | 메서드 | 설명 | |--------|-------------| | `textToSpeech(_:)` | 텍스트를 음성 오디오로 변환 | | `generateToFile(_:request:)` | 음성을 생성하고 로컬 파일로 바로 저장 | | `speak(_:voiceId:model:)` | 최소 매개변수로 간단한 TTS | | `speak(_:voiceId:model:emotion:intensity:)` | 감정 프리셋으로 TTS | | `getVoices(filter:)` | 선택적 필터로 사용 가능한 음성 가져오기 | | `getVoice(voiceId:)` | ID로 특정 음성 가져오기 | ### TTSRequest 필드 | 필드 | 타입 | 필수 | 설명 | |-------|------|----------|-------------| | `voiceId` | `String` | ✓ | 음성 ID (형식: `tc_*`) | | `text` | `String` | ✓ | 합성할 텍스트 (최대 2000자) | | `model` | `TTSModel` | ✓ | TTS 모델 (`.ssfmV21` 또는 `.ssfmV30`) | | `language` | `LanguageCode` | | 언어 코드 (생략 시 자동 감지) | | `prompt` | `TTSPrompt` | | 감정 설정 (`.basic`, `.preset`, 또는 `.smart`) | | `output` | `OutputSettings` | | 오디오 출력 설정 | | `seed` | `UInt32` | | 재현성을 위한 부호 없는 정수 시드 (≥ 0) | ### TTSResponse 필드 | 필드 | 타입 | 설명 | |-------|------|-------------| | `audioData` | `Data` | 생성된 오디오 데이터 | | `duration` | `TimeInterval` | 오디오 길이 (초) | | `format` | `AudioFormat` | 오디오 형식 (`.wav` 또는 `.mp3`) | ## 무음 길이 조절 이 기능은 **0.3.14 이상**에서 지원합니다. `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. 일반·스트리밍·타임스탬프 TTS는 `output.remove_silence_ms`, Compose는 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 전달합니다. 반환 타임스탬프는 처리 후 오디오를 기준으로 하며, 명시적인 `pause` 세그먼트는 유지됩니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. 아래 출력 설정을 해당 요청의 `output`에 전달하세요. 스트리밍은 스트리밍 전용 출력 타입을 사용합니다. ```swift let output = OutputSettings(removeSilenceMs: 300) let streamOutput = Typecast.OutputStream(removeSilenceMs: 300) ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Zig 타입캐스트 [API](https://studio.typecast.ai/developers/api)를 위한 공식 Zig 라이브러리입니다. AI 음성을 사용하여 텍스트를 자연스러운 음성으로 변환합니다. 순수 Zig 구현 - C 의존성 없음. Zig 표준 라이브러리의 `std.http.Client`와 `std.json`만 사용합니다. 타입캐스트 Zig SDK 소스 코드 Zig 패키지 (zig fetch) ## 설치 `zig fetch`로 의존성을 추가합니다: ```bash zig fetch --save "https://github.com/neosapience/typecast-sdk/archive/refs/tags/typecast-zig/v0.2.12.tar.gz" ``` 최신 등록 버전은 SDK Git 태그 기준 **typecast-zig/v0.2.12**입니다. 그런 다음 `build.zig`에 import를 추가합니다: ```zig const typecast_dep = b.dependency("typecast_zig", .{ .target = target, .optimize = optimize, }); exe.root_module.addImport("typecast", typecast_dep.module("typecast")); ``` ## 빠른 시작 ```zig const std = @import("std"); const typecast = @import("typecast"); pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); // 클라이언트 초기화 (환경변수에서 TYPECAST_API_KEY 읽기) var client = typecast.Client.init(allocator, .{ .api_key = std.posix.getenv("TYPECAST_API_KEY") orelse return error.MissingApiKey, }); defer client.deinit(); // 텍스트를 음성으로 변환 const response = try client.textToSpeech(.{ .voice_id = "tc_672c5f5ce59fac2a48faeaee", .text = "안녕하세요! 타입캐스트 Zig SDK입니다.", .model = .ssfm_v30, }); defer allocator.free(response.audio_data); // 오디오 파일 저장 const file = try std.fs.cwd().createFile("output.wav", .{}); defer file.close(); try file.writeAll(response.audio_data); std.debug.print("{d} 바이트 저장, 재생 시간: {d:.1}초\n", .{ response.audio_data.len, response.duration, }); } ``` ## 기능 - **다중 음성 모델**: `ssfm-v30` (최신) 및 `ssfm-v21` AI 음성 모델 지원 - **다국어 지원**: 영어, 한국어, 스페인어, 일본어, 중국어 등 35+개 언어 - **감정 제어**: 프리셋 감정 (normal, happy, sad, angry, whisper, toneup, tonedown) 또는 스마트 문맥 인식 추론 - **오디오 커스터마이징**: 음량 (LUFS -70 to 0), 피치 (-12 to +12 세미톤), 템포 (0.5x to 2.0x), 포맷 (WAV/MP3) 제어 - **보이스 탐색**: V2 Voices API로 모델, 성별, 나이, 용도별 필터링 - **순수 Zig**: 외부 의존성 없이 표준 라이브러리만 사용 - **타임스탬프 TTS**: 자막, 가라오케, 립싱크를 위한 단어·문자 단위 정렬 데이터 - **스트리밍**: 저지연 재생을 위한 실시간 청크 오디오 전송 (콜백 기반) - **명시적 메모리 관리**: 호출자 제공 allocator로 명확한 소유권 관리 ## 보이스 추천 원하는 스타일은 알지만 정확한 `voice_id`를 모를 때 `recommendVoices`를 사용합니다. ```zig const voices = try client.recommendVoices( "warm female voice for a product tutorial", 3, ); defer { for (voices) |voice| { allocator.free(voice.voice_id); allocator.free(voice.voice_name); } allocator.free(voices); } for (voices) |voice| { std.debug.print("{s} {s} {d:.3}\n", .{ voice.voice_id, voice.voice_name, voice.score, }); } ``` 추천 결과에는 `voice_id`, `voice_name`, `score`만 포함됩니다. 지원 모델, 감정, 성별, 연령대, 사용 사례 같은 상세 메타데이터가 필요하면 `getVoiceV2` 또는 `getVoicesV2`로 추가 조회하세요. ## 설정 환경변수 또는 직접 API 키를 전달할 수 있습니다: ```zig const typecast = @import("typecast"); // 환경변수 사용 (권장) // export TYPECAST_API_KEY="your-api-key-here" var client = typecast.Client.init(allocator, .{ .api_key = std.posix.getenv("TYPECAST_API_KEY") orelse return error.MissingApiKey, }); defer client.deinit(); ``` ```zig // 직접 전달 var client = typecast.Client.init(allocator, .{ .api_key = "your-api-key-here", }); defer client.deinit(); ``` 자체 프록시를 통해 요청하는 경우 `base_url`을 프록시 엔드포인트로 설정하고 `api_key`를 생략할 수 있습니다. API 키가 비어 있거나 없으면 SDK는 `X-API-KEY` 헤더를 보내지 않습니다. 기본 Typecast 호스트로 요청할 때는 API 키가 계속 필요합니다. ```zig API 키 없는 프록시 var client = typecast.Client.init(allocator, .{ .base_url = "https://your-proxy.example.com", }); defer client.deinit(); ``` ## 고급 사용법 ### 감정 제어 (ssfm-v30) ssfm-v30은 두 가지 감정 제어 모드를 제공합니다: **프리셋** 및 **스마트**. AI가 문맥에서 감정을 추론합니다: ```zig const response = try client.textToSpeech(.{ .voice_id = "tc_672c5f5ce59fac2a48faeaee", .text = "모든 것이 잘 될 거예요.", .model = .ssfm_v30, .prompt = .{ .smart = .{ .previous_text = "방금 최고의 소식을 들었어요!", .next_text = "축하하고 싶어요!", } }, }); defer allocator.free(response.audio_data); ``` 프리셋 값으로 감정을 명시적으로 설정합니다: ```zig const response = try client.textToSpeech(.{ .voice_id = "tc_672c5f5ce59fac2a48faeaee", .text = "이 기능들을 보여드리게 되어 정말 기쁩니다!", .model = .ssfm_v30, .prompt = .{ .preset = .{ .emotion_preset = .happy, .emotion_intensity = 1.5, } }, }); defer allocator.free(response.audio_data); ``` ### 오디오 커스터마이징 음량, 피치, 템포, 출력 포맷을 제어합니다: ```zig const response = try client.textToSpeech(.{ .voice_id = "tc_672c5f5ce59fac2a48faeaee", .text = "커스터마이징된 오디오 출력!", .model = .ssfm_v30, .output = .{ .target_lufs = -14.0, .audio_pitch = 2, .audio_tempo = 1.2, .audio_format = .mp3, }, .seed = 42, }); defer allocator.free(response.audio_data); ``` ### 파일로 바로 생성하기 `generateToFile`은 음성 합성과 파일 저장을 한 번에 처리합니다. `model`은 기본값으로 `ssfm-v30`을 사용하고, `.mp3` 또는 `.wav` 확장자로 출력 형식을 결정합니다. ```zig const response = try client.generateToFile("output.mp3", .{ .text = "안녕하세요, 타입캐스트입니다.", .voice_id = "tc_672c5f5ce59fac2a48faeaee", // voice_id는 https://studio.typecast.ai/developers/api/voices 에서 확인하세요. }); defer allocator.free(response.audio_data); ``` ### 텍스트만으로 쉼 표현 한 voice로 읽는 문장 안에 쉼만 넣고 싶다면 텍스트에 pause markup을 직접 작성합니다. `<|5s|>`, `<|1s|>`, `<|0.3s|>`, `<|0.34413s|>`처럼 쓰며 값은 초 단위이고 반드시 `s`로 끝납니다. 별도 pause 함수를 호출하지 않아도 텍스트만 보고 쉼 위치를 확인할 수 있습니다. ```zig var composer = client.composeSpeech(); try composer.defaults(.{ .voice_id = "tc_672c5f5ce59fac2a48faeaee", .model = .ssfm_v30 }); try composer.say("안녕하세요<|5s|>반갑습니다<|1s|>오늘<|2s|>날씨는 어떤 것 같으세요?", .{}); const audio = try composer.generate(allocator); defer allocator.free(audio.audio_data); ``` ### 다중 화자 합성 한 파일 안에서 서로 다른 voice나 구간별 pitch, tempo, prompt, seed 같은 옵션을 조합해야 할 때 사용합니다. composer는 세그먼트를 `POST /v1/text-to-speech/compose`로 보내며 WAV 또는 MP3를 직접 반환합니다. 무음 제거는 TTS 세그먼트에 명시적으로 설정하고, 명시적인 쉼은 유지됩니다. ```zig var composer = client.composeSpeech(); defer composer.deinit(); try composer.defaults(.{ .voice_id = "tc_672c5f5ce59fac2a48faeaee", .model = .ssfm_v30, }); try composer.say("Hello there", .{}); try composer.pause(5); try composer.say("Nice to meet you", .{ .voice_id = "tc_60e5426de8b95f1d3000d7b5", .output = .{ .audio_pitch = 2 }, }); try composer.pause(2); try composer.say("How does the weather feel?", .{}); const audio = try composer.generate(.wav); defer audio.deinit(allocator); try std.fs.cwd().writeFile(.{ .sub_path = "conversation.wav", .data = audio.audio_data }); ``` ### 보이스 탐색 (V2 API) 향상된 메타데이터와 함께 사용 가능한 보이스를 조회합니다: ```zig // 모든 보이스 조회 const voices = try client.getVoicesV2(null); defer allocator.free(voices); // 모델별 필터링 const filtered = try client.getVoicesV2(.{ .model = .ssfm_v30 }); defer allocator.free(filtered); for (voices) |voice| { std.debug.print("ID: {s}, 이름: {s}\n", .{ voice.voice_id, voice.voice_name }); } ``` ### 스트리밍 콜백을 통해 실시간으로 오디오 청크를 스트리밍합니다: ```zig try client.textToSpeechStream(.{ .voice_id = "tc_672c5f5ce59fac2a48faeaee", .text = "이 텍스트를 실시간으로 오디오로 스트리밍합니다.", .model = .ssfm_v30, }, struct { var first = true; fn onChunk(chunk: []const u8) anyerror!void { var data = chunk; if (first) { data = chunk[44..]; // 44바이트 WAV 헤더 건너뛰기 first = false; } // data는 32000 Hz 16비트 모노 원시 PCM // 오디오 출력으로 전달 } }.onChunk); ``` **WAV 스트리밍 형식:** 32000 Hz, 16비트, 모노 PCM. 첫 번째 청크에 44바이트 WAV 헤더(size = `0xFFFFFFFF`)가 포함되며, 이후 청크는 원시 PCM 데이터만 포함합니다. MP3 형식: 320 kbps, 44100 Hz, 각 청크는 독립적으로 디코딩 가능합니다. ## 타임스탬프 TTS `textToSpeechWithTimestamps()`는 `POST /v1/text-to-speech/with-timestamps`를 래핑하며, 오디오와 함께 단어·문자 단위 정렬 데이터를 반환합니다. 가라오케 하이라이트, 자막 생성, 립싱크 애플리케이션에 활용할 수 있습니다. ### 기본 사용법 ```zig const typecast = @import("typecast"); const std = @import("std"); pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); const client = try typecast.TypecastClient.init(allocator, "YOUR_API_KEY"); defer client.deinit(); const result = try client.textToSpeechWithTimestamps(.{ .voice_id = "tc_60e5426de8b95f1d3000d7b5", .text = "Hello. How are you?", .model = "ssfm-v30", }); defer result.deinit(); try std.fs.cwd().writeFile("output.wav", result.audioBytes()); std.debug.print("재생 시간: {d:.3}초\n", .{result.audio_duration}); for (result.words) |word| { std.debug.print(" [{d:.3}s – {d:.3}s] {s}\n", .{word.start_time, word.end_time, word.text}); } } ``` ### 정밀도(Granularity) 설정 `.granularity = .word`(기본값) 또는 `.granularity = .char`를 설정해 정렬 단위를 제어합니다. ```zig // 문자 단위 정렬 - 일본어·중국어에 필수 const result = try client.textToSpeechWithTimestamps(.{ .voice_id = "tc_60e5426de8b95f1d3000d7b5", .text = "Hello. How are you?", .model = "ssfm-v30", .granularity = .char, }); ``` ### 자막 내보내기 ```zig const srt = try result.toSrt(allocator); defer allocator.free(srt); try std.fs.cwd().writeFile("output.srt", srt); const vtt = try result.toVtt(allocator); defer allocator.free(vtt); try std.fs.cwd().writeFile("output.vtt", vtt); ``` **일본어·중국어:** 공백 구분자가 없는 언어(jpn, zho)는 단어 단위 세그먼트가 의미를 갖지 않습니다. 해당 언어에는 `.char` 정밀도를 사용해 문자 단위 정렬 데이터를 얻으세요. ## 지원 언어 35+개 언어를 지원하며 자동 언어 감지 기능을 제공합니다: | 코드 | 언어 | 코드 | 언어 | 코드 | 언어 | |------|------|------|------|------|------| | `eng` | 영어 | `jpn` | 일본어 | `ukr` | 우크라이나어 | | `kor` | 한국어 | `ell` | 그리스어 | `ind` | 인도네시아어 | | `spa` | 스페인어 | `tam` | 타밀어 | `dan` | 덴마크어 | | `deu` | 독일어 | `tgl` | 타갈로그어 | `swe` | 스웨덴어 | | `fra` | 프랑스어 | `fin` | 핀란드어 | `msa` | 말레이어 | | `ita` | 이탈리아어 | `zho` | 중국어 | `ces` | 체코어 | | `pol` | 폴란드어 | `slk` | 슬로바키아어 | `por` | 포르투갈어 | | `nld` | 네덜란드어 | `ara` | 아랍어 | `bul` | 불가리아어 | | `rus` | 러시아어 | `hrv` | 크로아티아어 | `ron` | 루마니아어 | | `ben` | 벵골어 | `hin` | 힌디어 | `hun` | 헝가리어 | | `nan` | 민난어 | `nor` | 노르웨이어 | `pan` | 펀자브어 | | `tha` | 태국어 | `tur` | 터키어 | `vie` | 베트남어 | | `yue` | 광둥어 | | | | | 언어를 지정하지 않으면 입력 텍스트에서 자동으로 감지됩니다. ## 에러 처리 SDK는 API 에러 처리를 위해 Zig의 error union을 사용합니다: ```zig const response = client.textToSpeech(.{ .voice_id = "tc_672c5f5ce59fac2a48faeaee", .text = "안녕하세요", .model = .ssfm_v30, }) catch |err| switch (err) { error.Unauthorized => { std.debug.print("유효하지 않은 API 키\n", .{}); return err; }, error.PaymentRequired => { std.debug.print("크레딧 부족\n", .{}); return err; }, error.RateLimited => { std.debug.print("요청 한도 초과 - 잠시 후 재시도\n", .{}); return err; }, else => return err, }; defer allocator.free(response.audio_data); ``` ### 에러 유형 | 에러 | 상태 코드 | 설명 | |------|-----------|------| | `error.BadRequest` | 400 | 잘못된 요청 파라미터 | | `error.Unauthorized` | 401 | 유효하지 않거나 누락된 API 키 | | `error.PaymentRequired` | 402 | 크레딧 부족 | | `error.NotFound` | 404 | 리소스를 찾을 수 없음 | | `error.UnprocessableEntity` | 422 | 유효성 검사 오류 | | `error.RateLimited` | 429 | 요청 한도 초과 | | `error.InternalServerError` | 500 | 서버 오류 | | `error.JsonParseError` | - | JSON 파싱 오류 | ## API 레퍼런스 ### 클라이언트 메서드 | 메서드 | 설명 | |--------|------| | `init(allocator, config)` | 설정으로 클라이언트 생성 | | `deinit()` | 클라이언트 리소스 정리 | | `textToSpeech(request)` | 텍스트를 음성 오디오로 변환 | | `generateToFile(path, request)` | 음성을 생성하고 로컬 파일로 바로 저장 | | `textToSpeechStream(request, callback)` | 콜백을 통한 오디오 청크 스트리밍 | | `getMySubscription()` | 구독 정보 조회 | | `getVoices(model)` | 사용 가능한 보이스 조회 (V1) | | `getVoicesV2(filter)` | 메타데이터와 함께 보이스 조회 (V2) | | `getVoiceV2(voice_id, model)` | 특정 보이스 조회 | ## 무음 길이 조절 이 기능은 **0.2.12 이상**에서 지원합니다. `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. 일반·스트리밍·타임스탬프 TTS는 `output.remove_silence_ms`, Compose는 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 전달합니다. 반환 타임스탬프는 처리 후 오디오를 기준으로 하며, 명시적인 `pause` 세그먼트는 유지됩니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. 아래 출력 설정을 해당 요청의 `output`에 전달하세요. 스트리밍은 스트리밍 전용 출력 타입을 사용합니다. ```zig const output = typecast.models.Output{ .remove_silence_ms = 300 }; const stream_output = typecast.models.OutputStream{ .remove_silence_ms = 300 }; ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # PHP 타입캐스트 [API](https://studio.typecast.ai/developers/api)를 위한 공식 PHP 라이브러리입니다. AI 음성을 사용하여 텍스트를 자연스러운 음성으로 변환합니다. Guzzle 7 기반의 안정적인 HTTP 통신. PHP 8.1+ 및 Composer 필요. 타입캐스트 PHP SDK 타입캐스트 PHP SDK 소스 코드 ## 설치 Composer로 설치합니다: ```bash composer require neosapience/typecast-php:0.1.14 ``` 권장 PHP SDK 릴리스는 Packagist의 **v0.1.14**이며, 위 명령에서 이 버전을 지정합니다. **PHP 8.1 이상** 및 Composer가 필요합니다. `php -v`로 버전을 확인하세요. ## 빠른 시작 ```php textToSpeech(new TTSRequest( voiceId: 'tc_672c5f5ce59fac2a48faeaee', text: '안녕하세요! 타입캐스트 PHP SDK입니다.', model: 'ssfm-v30', )); // 오디오 파일 저장 file_put_contents('output.wav', $response->audioData); echo "재생 시간: {$response->duration}초, 포맷: {$response->format}\n"; ``` ## 기능 - **다중 음성 모델**: `ssfm-v30` (최신) 및 `ssfm-v21` AI 음성 모델 지원 - **다국어 지원**: 영어, 한국어, 스페인어, 일본어, 중국어 등 35+개 언어 - **감정 제어**: 프리셋 감정 (normal, happy, sad, angry, whisper, toneup, tonedown) 또는 스마트 문맥 인식 추론 - **오디오 커스터마이징**: 음량 (LUFS -70 to 0), 피치 (-12 to +12 세미톤), 템포 (0.5x to 2.0x), 포맷 (WAV/MP3) 제어 - **보이스 탐색**: V2 Voices API로 모델, 성별, 나이, 용도별 필터링 - **타임스탬프 TTS**: 자막, 가라오케, 립싱크를 위한 단어·문자 단위 정렬 데이터 - **스트리밍**: 저지연 재생을 위한 실시간 청크 오디오 전송 (콜백 기반) - **Guzzle 7**: 업계 표준 HTTP 클라이언트 - **타입 안전성**: 타입 프로퍼티와 네임드 아규먼트 (PHP 8.1+) ## 보이스 추천 원하는 스타일은 알지만 정확한 `voice_id`를 모를 때 `recommendVoices`를 사용합니다. ```php $voices = $client->recommendVoices( 'warm female voice for a product tutorial', count: 3, ); foreach ($voices as $voice) { echo "{$voice->voiceId} {$voice->voiceName} {$voice->score}\n"; } ``` 추천 결과에는 `voiceId`, `voiceName`, `score`만 포함됩니다. 지원 모델, 감정, 성별, 연령대, 사용 사례 같은 상세 메타데이터가 필요하면 `getVoiceV2` 또는 `getVoicesV2`로 추가 조회하세요. ## 설정 환경변수 또는 직접 API 키를 전달할 수 있습니다: ```bash 환경변수 export TYPECAST_API_KEY="your-api-key-here" ``` ```php 환경변수에서 읽기 use Neosapience\Typecast\TypecastClient; $client = new TypecastClient( apiKey: getenv('TYPECAST_API_KEY'), ); ``` ```php 직접 전달 use Neosapience\Typecast\TypecastClient; $client = new TypecastClient( apiKey: 'your-api-key-here', ); ``` 자체 프록시를 통해 요청하는 경우 `baseUrl`을 프록시 엔드포인트로 설정하고 `apiKey`를 생략할 수 있습니다. API 키가 비어 있거나 없으면 SDK는 `X-API-KEY` 헤더를 보내지 않습니다. 기본 Typecast 호스트로 요청할 때는 API 키가 계속 필요합니다. ```php API 키 없는 프록시 $client = new TypecastClient( baseUrl: 'https://your-proxy.example.com', ); ``` ## 고급 사용법 ### 감정 제어 (ssfm-v30) ssfm-v30은 두 가지 감정 제어 모드를 제공합니다: **프리셋** 및 **스마트**. AI가 문맥에서 감정을 추론합니다: ```php use Neosapience\Typecast\Models\{TTSRequest, SmartPrompt}; $response = $client->textToSpeech(new TTSRequest( voiceId: 'tc_672c5f5ce59fac2a48faeaee', text: '모든 것이 잘 될 거예요.', model: 'ssfm-v30', prompt: new SmartPrompt( previousText: '방금 최고의 소식을 들었어요!', nextText: '축하하고 싶어요!', ), )); ``` 프리셋 값으로 감정을 명시적으로 설정합니다: ```php use Neosapience\Typecast\Models\{TTSRequest, PresetPrompt}; $response = $client->textToSpeech(new TTSRequest( voiceId: 'tc_672c5f5ce59fac2a48faeaee', text: '이 기능들을 보여드리게 되어 정말 기쁩니다!', model: 'ssfm-v30', prompt: new PresetPrompt( emotionPreset: 'happy', emotionIntensity: 1.5, ), )); ``` ### 오디오 커스터마이징 음량, 피치, 템포, 출력 포맷을 제어합니다: ```php use Neosapience\Typecast\Models\{TTSRequest, Output}; $response = $client->textToSpeech(new TTSRequest( voiceId: 'tc_672c5f5ce59fac2a48faeaee', text: '커스터마이징된 오디오 출력!', model: 'ssfm-v30', output: new Output( targetLufs: -14.0, audioPitch: 2, audioTempo: 1.2, audioFormat: 'mp3', ), seed: 42, )); file_put_contents('output.mp3', $response->audioData); ``` ### 파일로 바로 생성하기 `generateToFile`은 음성 합성과 파일 저장을 한 번에 처리합니다. `model`은 기본값으로 `ssfm-v30`을 사용하고, `.mp3` 또는 `.wav` 확장자로 출력 형식을 결정합니다. ```php $client->generateToFile( 'hello.mp3', '안녕하세요, 타입캐스트입니다.', 'tc_672c5f5ce59fac2a48faeaee' // voice_id는 https://studio.typecast.ai/developers/api/voices 에서 확인하세요. ); ``` ### 텍스트만으로 쉼 표현 한 voice로 읽는 문장 안에 쉼만 넣고 싶다면 텍스트에 pause markup을 직접 작성합니다. `<|5s|>`, `<|1s|>`, `<|0.3s|>`, `<|0.34413s|>`처럼 쓰며 값은 초 단위이고 반드시 `s`로 끝납니다. 별도 pause 함수를 호출하지 않아도 텍스트만 보고 쉼 위치를 확인할 수 있습니다. ```php $audio = $client->composeSpeech() ->defaults(new ComposerSettings(voiceId: 'tc_672c5f5ce59fac2a48faeaee', model: 'ssfm-v30')) ->say('안녕하세요<|5s|>반갑습니다<|1s|>오늘<|2s|>날씨는 어떤 것 같으세요?') ->generate(); ``` ### 다중 화자 합성 한 파일 안에서 서로 다른 voice나 구간별 pitch, tempo, prompt, seed 같은 옵션을 조합해야 할 때 사용합니다. composer는 세그먼트를 `POST /v1/text-to-speech/compose`로 보내며 WAV 또는 MP3를 직접 반환합니다. 무음 제거는 TTS 세그먼트에 명시적으로 설정하고, 명시적인 쉼은 유지됩니다. ```php use Neosapience\Typecast\ComposerSettings; use Neosapience\Typecast\Models\Output; $audio = $client->composeSpeech() ->defaults(new ComposerSettings(voiceId: 'tc_672c5f5ce59fac2a48faeaee', model: 'ssfm-v30')) ->say('Hello there') ->pause(5.0) ->say('Nice to meet you', new ComposerSettings( voiceId: 'tc_60e5426de8b95f1d3000d7b5', output: new Output(audioPitch: 2) )) ->say('Today') ->pause(2.0) ->say('How does the weather feel?') ->generate(); file_put_contents('conversation.wav', $audio->audioData); ``` ### 보이스 탐색 (V2 API) 향상된 메타데이터와 함께 사용 가능한 보이스를 조회합니다: ```php use Neosapience\Typecast\Models\VoicesV2Filter; // 모든 보이스 조회 $voices = $client->getVoicesV2(); // 필터링 $filtered = $client->getVoicesV2(new VoicesV2Filter( model: 'ssfm-v30', gender: 'female', age: 'young_adult', )); foreach ($voices as $voice) { echo "ID: {$voice->voiceId}, 이름: {$voice->voiceName}\n"; } // 특정 보이스 조회 $voice = $client->getVoiceV2('tc_672c5f5ce59fac2a48faeaee'); ``` ### 스트리밍 콜백을 통해 실시간으로 오디오 청크를 스트리밍합니다: ```php use Neosapience\Typecast\Models\TTSRequestStream; $first = true; $client->textToSpeechStream( new TTSRequestStream( voiceId: 'tc_672c5f5ce59fac2a48faeaee', text: '이 텍스트를 실시간으로 오디오로 스트리밍합니다.', model: 'ssfm-v30', ), function (string $chunk) use (&$first): void { if ($first) { $chunk = substr($chunk, 44); // 44바이트 WAV 헤더 건너뛰기 $first = false; } // $chunk는 32000 Hz 16비트 모노 원시 PCM // 오디오 출력으로 전달 또는 ffplay로 파이핑 }, ); ``` **WAV 스트리밍 형식:** 32000 Hz, 16비트, 모노 PCM. 첫 번째 청크에 44바이트 WAV 헤더(size = `0xFFFFFFFF`)가 포함되며, 이후 청크는 원시 PCM 데이터만 포함합니다. MP3 형식: 320 kbps, 44100 Hz, 각 청크는 독립적으로 디코딩 가능합니다. ## 타임스탬프 TTS `textToSpeechWithTimestamps()`는 `POST /v1/text-to-speech/with-timestamps`를 래핑하며, 오디오와 함께 단어·문자 단위 정렬 데이터를 반환합니다. 가라오케 하이라이트, 자막 생성, 립싱크 애플리케이션에 활용할 수 있습니다. ### 기본 사용법 ```php textToSpeechWithTimestamps(new TTSRequestWithTimestamps([ 'voice_id' => 'tc_60e5426de8b95f1d3000d7b5', 'text' => 'Hello. How are you?', 'model' => 'ssfm-v30', ])); file_put_contents('output.wav', $result->audioBytes()); printf("재생 시간: %.3f초\n", $result->audioDuration); foreach ($result->words as $word) { printf(" [%.3fs – %.3fs] %s\n", $word->startTime, $word->endTime, $word->text); } ``` ### 정밀도(Granularity) 설정 `'granularity' => 'word'`(기본값) 또는 `'granularity' => 'char'`를 설정해 정렬 단위를 제어합니다. ```php // 문자 단위 정렬 - 일본어·중국어에 필수 $result = $client->textToSpeechWithTimestamps(new TTSRequestWithTimestamps([ 'voice_id' => 'tc_60e5426de8b95f1d3000d7b5', 'text' => 'Hello. How are you?', 'model' => 'ssfm-v30', 'granularity' => 'char', ])); ``` ### 자막 내보내기 ```php $srt = $result->toSrt(); file_put_contents('output.srt', $srt); $vtt = $result->toVtt(); file_put_contents('output.vtt', $vtt); ``` **일본어·중국어:** 공백 구분자가 없는 언어(jpn, zho)는 단어 단위 세그먼트가 의미를 갖지 않습니다. 해당 언어에는 `'granularity' => 'char'`를 사용해 문자 단위 정렬 데이터를 얻으세요. ## 지원 언어 35+개 언어를 지원하며 자동 언어 감지 기능을 제공합니다: | 코드 | 언어 | 코드 | 언어 | 코드 | 언어 | |------|------|------|------|------|------| | `eng` | 영어 | `jpn` | 일본어 | `ukr` | 우크라이나어 | | `kor` | 한국어 | `ell` | 그리스어 | `ind` | 인도네시아어 | | `spa` | 스페인어 | `tam` | 타밀어 | `dan` | 덴마크어 | | `deu` | 독일어 | `tgl` | 타갈로그어 | `swe` | 스웨덴어 | | `fra` | 프랑스어 | `fin` | 핀란드어 | `msa` | 말레이어 | | `ita` | 이탈리아어 | `zho` | 중국어 | `ces` | 체코어 | | `pol` | 폴란드어 | `slk` | 슬로바키아어 | `por` | 포르투갈어 | | `nld` | 네덜란드어 | `ara` | 아랍어 | `bul` | 불가리아어 | | `rus` | 러시아어 | `hrv` | 크로아티아어 | `ron` | 루마니아어 | | `ben` | 벵골어 | `hin` | 힌디어 | `hun` | 헝가리어 | | `nan` | 민난어 | `nor` | 노르웨이어 | `pan` | 펀자브어 | | `tha` | 태국어 | `tur` | 터키어 | `vie` | 베트남어 | | `yue` | 광둥어 | | | | | 언어를 지정하지 않으면 입력 텍스트에서 자동으로 감지됩니다. ## 에러 처리 SDK는 HTTP 에러별 구체적인 예외를 발생시킵니다: ```php use Neosapience\Typecast\Exceptions\{ TypecastException, UnauthorizedException, PaymentRequiredException, RateLimitException, }; try { $response = $client->textToSpeech($request); } catch (UnauthorizedException $e) { echo "유효하지 않은 API 키: {$e->getMessage()}\n"; } catch (PaymentRequiredException $e) { echo "크레딧 부족\n"; } catch (RateLimitException $e) { echo "요청 한도 초과 - 잠시 후 재시도\n"; } catch (TypecastException $e) { echo "에러: {$e->getMessage()}\n"; } ``` | 예외 | 상태 코드 | 설명 | |------|-----------|------| | `BadRequestException` | 400 | 잘못된 요청 파라미터 | | `UnauthorizedException` | 401 | 유효하지 않거나 누락된 API 키 | | `PaymentRequiredException` | 402 | 크레딧 부족 | | `NotFoundException` | 404 | 리소스를 찾을 수 없음 | | `UnprocessableEntityException` | 422 | 유효성 검사 오류 | | `RateLimitException` | 429 | 요청 한도 초과 | | `InternalServerException` | 500 | 서버 오류 | ## API 레퍼런스 ### TypecastClient 메서드 | 메서드 | 설명 | |--------|------| | `textToSpeech(TTSRequest)` | 텍스트를 음성 오디오로 변환 | | `generateToFile(path, text, voiceId)` | 음성을 생성하고 로컬 파일로 바로 저장 | | `textToSpeechStream(TTSRequestStream, callable)` | 콜백을 통한 오디오 청크 스트리밍 | | `getMySubscription()` | 구독 정보 조회 | | `getVoices(?string $model)` | 사용 가능한 보이스 조회 (V1) | | `getVoicesV2(?VoicesV2Filter)` | 메타데이터와 함께 보이스 조회 (V2) | | `getVoiceV2(string $voiceId)` | 특정 보이스 조회 | ## 무음 길이 조절 이 기능은 **0.1.14 이상**에서 지원합니다. `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. 일반·스트리밍·타임스탬프 TTS는 `output.remove_silence_ms`, Compose는 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 전달합니다. 반환 타임스탬프는 처리 후 오디오를 기준으로 하며, 명시적인 `pause` 세그먼트는 유지됩니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. 아래 출력 설정을 해당 요청의 `output`에 전달하세요. 스트리밍은 스트리밍 전용 출력 타입을 사용합니다. ```php $output = new \Neosapience\Typecast\Models\Output(removeSilenceMs: 300); $streamOutput = new \Neosapience\Typecast\Models\OutputStream(removeSilenceMs: 300); ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Dart/Flutter 타입캐스트 [API](https://studio.typecast.ai/developers/api)를 위한 공식 Dart 및 Flutter SDK입니다. Dart 또는 Flutter 애플리케이션에서 텍스트를 음성으로 변환하고, 스트리밍, 타임스탬프, 보이스 조회, 커스텀 보이스 생성 기능을 사용할 수 있습니다. 타입캐스트 Dart SDK 타입캐스트 Dart SDK 소스 코드 ## 설치 pub.dev에서 설치합니다: ```bash dart pub add typecast_dart ``` Flutter 프로젝트에서는 다음 명령을 사용합니다: ```bash flutter pub add typecast_dart flutter pub add audioplayers ``` 최신 등록 버전은 pub.dev 기준 **0.1.13**입니다. **typecast_dart 0.1.13 이상**을 사용하세요. 프로덕션 Flutter 앱에서는 장기 API 키를 배포되는 클라이언트에 직접 포함하지 않는 것을 권장합니다. API 키를 비공개로 유지해야 한다면 백엔드를 통해 요청을 프록시하세요. ## 빠른 시작 ```dart import 'package:audioplayers/audioplayers.dart'; import 'package:typecast_dart/typecast_dart.dart'; final client = TypecastClient(apiKey: 'YOUR_API_KEY'); final player = AudioPlayer(); Future speakAndPlay() async { final response = await client.textToSpeech( const TtsRequest( voiceId: 'tc_672c5f5ce59fac2a48faeaee', text: '안녕하세요! 타입캐스트 Dart SDK입니다.', model: TtsModel.ssfmV30, language: LanguageCode.kor, output: Output(audioFormat: AudioFormat.wav), ), ); await player.play(BytesSource(response.audioData)); print('재생 시간: ${response.duration}초, 포맷: ${response.format.value}'); } ``` ## Flutter에서 오디오 재생하기 Dart SDK는 생성된 오디오를 바이트 데이터로 반환합니다. Flutter에서는 `audioplayers` 같은 오디오 재생 패키지에 이 바이트 데이터를 전달해 바로 재생할 수 있습니다. 하나의 `AudioPlayer` 인스턴스를 공유하고, 각 응답을 메모리에서 바로 재생합니다: ```dart import 'package:audioplayers/audioplayers.dart'; import 'package:typecast_dart/typecast_dart.dart'; final client = TypecastClient(apiKey: 'YOUR_API_KEY'); final player = AudioPlayer(); Future playTts(String text) async { final response = await client.textToSpeech( TtsRequest( voiceId: 'tc_672c5f5ce59fac2a48faeaee', text: text, model: TtsModel.ssfmV30, language: LanguageCode.kor, output: const Output(audioFormat: AudioFormat.wav), ), ); await player.play(BytesSource(response.audioData)); } ``` 프로덕션 Flutter 앱에서는 장기 API 키를 백엔드에 보관하는 것을 권장합니다. Flutter 앱은 백엔드에서 생성된 오디오 바이트를 받아와도 같은 방식으로 `BytesSource`에 전달해 재생할 수 있습니다. ## 기능 - **다중 음성 모델**: `ssfm-v30` 및 `ssfm-v21` AI 음성 모델 지원 - **다국어 지원**: 영어, 한국어, 일본어, 중국어, 스페인어 등 35+개 언어 - **감정 제어**: 프리셋 감정 또는 스마트 문맥 인식 추론 - **오디오 커스터마이징**: 음량, 피치, 템포, 출력 포맷 제어 - **보이스 탐색**: V2 Voices API로 모델, 성별, 나이, 용도별 필터링 - **스트리밍**: Dart `Stream>`로 스트리밍 TTS 응답 사용 - **타임스탬프 TTS**: SRT/VTT 헬퍼가 포함된 단어·문자 단위 정렬 데이터 - **즉시 보이스 클로닝**: WAV 샘플을 업로드해 커스텀 보이스 ID 생성 - **Dart 및 Flutter 지원**: Dart CLI, 서버, Flutter 프로젝트에서 동일 패키지 사용 ## 보이스 추천 원하는 스타일은 알지만 정확한 `voice_id`를 모를 때 `recommendVoices`를 사용합니다. ```dart final voices = await client.recommendVoices( 'warm female voice for a product tutorial', count: 3, ); for (final voice in voices) { print('${voice.voiceId} ${voice.voiceName} ${voice.score}'); } ``` 추천 결과에는 `voiceId`, `voiceName`, `score`만 포함됩니다. 지원 모델, 감정, 성별, 연령대, 사용 사례 같은 상세 메타데이터가 필요하면 `getVoiceV2` 또는 `getVoicesV2`로 추가 조회하세요. ## 설정 환경변수 또는 생성자에 API 키를 직접 전달할 수 있습니다: ```bash 환경변수 export TYPECAST_API_KEY="your-api-key-here" ``` ```dart 환경변수에서 읽기 import 'dart:io'; import 'package:typecast_dart/typecast_dart.dart'; final client = TypecastClient( apiKey: Platform.environment['TYPECAST_API_KEY'], ); ``` ```dart 직접 전달 import 'package:typecast_dart/typecast_dart.dart'; final client = TypecastClient( apiKey: 'your-api-key-here', ); ``` 자체 프록시를 통해 요청하는 경우 `baseUrl`을 프록시 엔드포인트로 설정하고 `apiKey`를 생략할 수 있습니다. API 키가 비어 있거나 없으면 SDK는 `X-API-KEY` 헤더를 보내지 않습니다. 기본 Typecast 호스트로 요청할 때는 API 키가 계속 필요합니다. ```dart API 키 없는 프록시 final client = TypecastClient( baseUrl: 'https://your-proxy.example.com', ); ``` ## 고급 사용법 ### 감정 제어 (ssfm-v30) ssfm-v30은 두 가지 감정 제어 모드를 제공합니다: **프리셋** 및 **스마트**. AI가 문맥에서 감정을 추론합니다: ```dart final response = await client.textToSpeech( const TtsRequest( voiceId: 'tc_672c5f5ce59fac2a48faeaee', text: '모든 것이 잘 될 거예요.', model: TtsModel.ssfmV30, prompt: SmartPrompt( previousText: '방금 최고의 소식을 들었어요!', nextText: '축하하고 싶어요!', ), ), ); await player.play(BytesSource(response.audioData)); ``` 프리셋 값으로 감정을 명시적으로 설정합니다: ```dart final response = await client.textToSpeech( const TtsRequest( voiceId: 'tc_672c5f5ce59fac2a48faeaee', text: '이 기능들을 보여드리게 되어 정말 기쁩니다!', model: TtsModel.ssfmV30, prompt: PresetPrompt( emotionPreset: EmotionPreset.happy, emotionIntensity: 1.5, ), ), ); await player.play(BytesSource(response.audioData)); ``` ### 오디오 커스터마이징 음량, 피치, 템포, 출력 포맷을 제어합니다: ```dart final response = await client.textToSpeech( const TtsRequest( voiceId: 'tc_672c5f5ce59fac2a48faeaee', text: '커스터마이징된 오디오 출력!', model: TtsModel.ssfmV30, output: Output( targetLufs: -14.0, audioPitch: 2, audioTempo: 1.2, audioFormat: AudioFormat.mp3, ), seed: 42, ), ); await player.play(BytesSource(response.audioData)); ``` ### 파일로 바로 생성하기 `generateToFile`은 음성 합성과 파일 저장을 한 번에 처리합니다. `model`은 기본값으로 `ssfm-v30`을 사용하고, `.mp3` 또는 `.wav` 확장자로 출력 형식을 결정합니다. ```dart await client.generateToFile( 'output.mp3', GenerateToFileRequest( text: '안녕하세요, 타입캐스트입니다.', voiceId: 'tc_672c5f5ce59fac2a48faeaee', // voice_id는 https://studio.typecast.ai/developers/api/voices 에서 확인하세요. ), ); ``` ### 텍스트만으로 쉼 표현 한 voice로 읽는 문장 안에 쉼만 넣고 싶다면 텍스트에 pause markup을 직접 작성합니다. `<|5s|>`, `<|1s|>`, `<|0.3s|>`, `<|0.34413s|>`처럼 쓰며 값은 초 단위이고 반드시 `s`로 끝납니다. 별도 pause 함수를 호출하지 않아도 텍스트만 보고 쉼 위치를 확인할 수 있습니다. ```dart final audio = await client .composeSpeech() .defaults(ComposerSettings(voiceId: 'tc_672c5f5ce59fac2a48faeaee', model: TTSModel.ssfmV30)) .say('안녕하세요<|5s|>반갑습니다<|1s|>오늘<|2s|>날씨는 어떤 것 같으세요?') .generate(); ``` ### 다중 화자 합성 한 파일 안에서 서로 다른 voice나 구간별 pitch, tempo, prompt, seed 같은 옵션을 조합해야 할 때 사용합니다. composer는 세그먼트를 `POST /v1/text-to-speech/compose`로 보내며 WAV 또는 MP3를 직접 반환합니다. 무음 제거는 TTS 세그먼트에 명시적으로 설정하고, 명시적인 쉼은 유지됩니다. ```dart final audio = await client .composeSpeech() .defaults(const ComposerSettings(voiceId: 'tc_672c5f5ce59fac2a48faeaee', model: TtsModel.ssfmV30)) .say('Hello there') .pause(5) .say( 'Nice to meet you', overrides: const ComposerSettings( voiceId: 'tc_60e5426de8b95f1d3000d7b5', output: Output(audioPitch: 2), ), ) .say('Today') .pause(2) .say('How does the weather feel?') .generate(); await File('conversation.wav').writeAsBytes(audio.audioData); ``` ### 보이스 탐색 (V2 API) 향상된 메타데이터와 함께 사용 가능한 보이스를 조회합니다: ```dart final voices = await client.getVoicesV2(); final filtered = await client.getVoicesV2( const VoicesV2Filter( model: TtsModel.ssfmV30, gender: 'female', age: 'young_adult', ), ); for (final voice in voices) { print('ID: ${voice.voiceId}, 이름: ${voice.voiceName}'); print('성별: ${voice.gender}, 나이: ${voice.age}'); } final voice = await client.getVoiceV2('tc_672c5f5ce59fac2a48faeaee'); print(voice.voiceName); ``` ### 스트리밍 Dart 스트림으로 오디오를 수신하고 파일 저장 없이 재생합니다: ```dart import 'dart:typed_data'; final stream = await client.textToSpeechStream( const TtsRequestStream( voiceId: 'tc_672c5f5ce59fac2a48faeaee', text: '이 텍스트를 실시간으로 오디오로 스트리밍합니다.', model: TtsModel.ssfmV30, output: OutputStream(audioFormat: AudioFormat.wav), ), ); final audioBytes = []; await for (final chunk in stream) { audioBytes.addAll(chunk); } await player.play(BytesSource(Uint8List.fromList(audioBytes))); ``` **WAV 스트리밍 형식:** 32000 Hz, 16비트, 모노 PCM. 첫 번째 청크에 44바이트 WAV 헤더(size = `0xFFFFFFFF`)가 포함되며, 이후 청크는 원시 PCM 데이터만 포함합니다. 위 예제는 파일 저장을 피하고 전체 스트림을 메모리에서 재생합니다. 진짜 저지연 청크 단위 재생이 필요하다면 `audioplayers` 대신 PCM 청크를 직접 공급할 수 있는 스트리밍 오디오 엔진을 사용하세요. ## 타임스탬프 TTS `textToSpeechWithTimestamps()`는 `POST /v1/text-to-speech/with-timestamps`를 래핑하며, 오디오와 함께 단어·문자 단위 정렬 데이터를 반환합니다. ```dart final result = await client.textToSpeechWithTimestamps( const TtsRequest( voiceId: 'tc_60e5426de8b95f1d3000d7b5', text: '안녕하세요. 반갑습니다.', model: TtsModel.ssfmV30, language: LanguageCode.kor, ), ); await player.play(BytesSource(result.audioBytes())); print('재생 시간: ${result.audioDuration}초'); for (final word in result.words) { print('[${word.startTime}초 - ${word.endTime}초] ${word.word}'); } ``` ### 정밀도(Granularity) 설정 `granularity: 'word'`(기본값) 또는 `granularity: 'char'`를 설정해 정렬 단위를 제어합니다. ```dart final result = await client.textToSpeechWithTimestamps( const TtsRequest( voiceId: 'tc_60e5426de8b95f1d3000d7b5', text: '안녕하세요. 반갑습니다.', model: TtsModel.ssfmV30, language: LanguageCode.kor, ), granularity: 'char', ); ``` ### 자막 내보내기 ```dart await File('output.srt').writeAsString(result.toSrt()); await File('output.vtt').writeAsString(result.toVtt()); ``` ## 즉시 보이스 클로닝 짧은 WAV 샘플을 업로드해 커스텀 보이스를 생성합니다: ```dart final voice = await client.cloneVoice( audio: await File('sample.wav').readAsBytes(), filename: 'sample.wav', name: 'My Voice', model: TtsModel.ssfmV30, ); print('커스텀 보이스 ID: ${voice.voiceId}'); ``` 보이스 클로닝 오디오는 **25 MB 이하**여야 하며, 커스텀 보이스 이름은 **1-30자**여야 합니다. ## 무음 길이 조절 이 기능은 **0.1.13 이상**에서 지원합니다. `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. 일반·스트리밍·타임스탬프 TTS는 `output.remove_silence_ms`, Compose는 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 전달합니다. 반환 타임스탬프는 처리 후 오디오를 기준으로 하며, 명시적인 `pause` 세그먼트는 유지됩니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. 아래 출력 설정을 해당 요청의 `output`에 전달하세요. 스트리밍은 스트리밍 전용 출력 타입을 사용합니다. ```dart final output = Output(removeSilenceMs: 300); final streamOutput = OutputStream(removeSilenceMs: 300); ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Ruby 타입캐스트 [API](https://studio.typecast.ai/developers/api)를 위한 공식 Ruby SDK입니다. AI 음성을 사용해 텍스트를 자연스러운 음성으로 변환하고, 타임스탬프, 보이스 조회, 커스텀 보이스 생성 기능을 사용할 수 있습니다. Ruby SDK는 런타임 의존성 없이 Ruby 표준 라이브러리만 사용하며 Ruby 2.6 이상을 지원합니다. 타입캐스트 Ruby SDK 타입캐스트 Ruby SDK 소스 코드 ## 설치 RubyGems에서 설치합니다: ```bash gem install typecast-ruby ``` 또는 Gemfile에 추가합니다: ```ruby gem "typecast-ruby", "~> 0.1.11" ``` 최신 등록 버전은 RubyGems 기준 **0.1.11**입니다. **Ruby 2.6 이상**이 필요합니다. `ruby -v`로 버전을 확인하세요. ## 빠른 시작 ```ruby require "typecast" client = Typecast::Client.new(api_key: ENV["TYPECAST_API_KEY"]) response = client.text_to_speech( Typecast::Models::TTSRequest.new( voice_id: "tc_672c5f5ce59fac2a48faeaee", text: "안녕하세요! 타입캐스트 Ruby SDK입니다.", model: Typecast::Models::TTS_MODEL_V30, language: "kor", output: Typecast::Models::Output.new(audio_format: "wav") ) ) File.binwrite("output.wav", response.audio_data) puts "재생 시간: #{response.duration}초, 포맷: #{response.format}" ``` ## 기능 - **다중 음성 모델**: `ssfm-v30` 및 `ssfm-v21` AI 음성 모델 지원 - **다국어 지원**: 영어, 한국어, 일본어, 중국어, 스페인어 등 35+개 언어 - **감정 제어**: 프리셋 감정 또는 스마트 문맥 인식 추론 - **오디오 커스터마이징**: 음량, 피치, 템포, 출력 포맷 제어 - **보이스 탐색**: V2 Voices API로 모델, 성별, 나이, 용도별 필터링 - **스트리밍 엔드포인트**: Ruby에서 스트리밍 TTS 응답 사용 - **타임스탬프 TTS**: SRT/VTT 헬퍼가 포함된 단어·문자 단위 정렬 데이터 - **즉시 보이스 클로닝**: WAV 샘플을 업로드해 커스텀 보이스 ID 생성 - **런타임 의존성 없음**: Ruby 표준 라이브러리 `net/http` 기반 ## 보이스 추천 원하는 스타일은 알지만 정확한 `voice_id`를 모를 때 `recommend_voices`를 사용합니다. ```ruby voices = client.recommend_voices( "warm female voice for a product tutorial", count: 3 ) voices.each do |voice| puts "#{voice.voice_id} #{voice.voice_name} #{voice.score}" end ``` 추천 결과에는 `voice_id`, `voice_name`, `score`만 포함됩니다. 최신 보이스 메타데이터가 필요하면 `get_voice_v3` 또는 `get_voices_v3`로 조회하세요. ## 설정 환경변수 또는 생성자에 API 키를 직접 전달할 수 있습니다: ```bash 환경변수 export TYPECAST_API_KEY="your-api-key-here" ``` ```ruby 환경변수에서 읽기 require "typecast" client = Typecast::Client.new( api_key: ENV["TYPECAST_API_KEY"] ) ``` ```ruby 직접 전달 require "typecast" client = Typecast::Client.new( api_key: "your-api-key-here" ) ``` 자체 프록시를 통해 요청하는 경우 `base_url`을 프록시 엔드포인트로 설정하고 `api_key`를 생략할 수 있습니다. API 키가 비어 있거나 없으면 SDK는 `X-API-KEY` 헤더를 보내지 않습니다. 기본 Typecast 호스트로 요청할 때는 API 키가 계속 필요합니다. ```ruby API 키 없는 프록시 client = Typecast::Client.new( base_url: "https://your-proxy.example.com" ) ``` API 호스트와 HTTP 타임아웃도 설정할 수 있습니다: ```ruby client = Typecast::Client.new( api_key: ENV["TYPECAST_API_KEY"], base_url: "https://api.typecast.ai", open_timeout: 10, read_timeout: 30 ) ``` ## 고급 사용법 ### 감정 제어 (ssfm-v30) ssfm-v30은 두 가지 감정 제어 모드를 제공합니다: **프리셋** 및 **스마트**. AI가 문맥에서 감정을 추론합니다: ```ruby response = client.text_to_speech( Typecast::Models::TTSRequest.new( voice_id: "tc_672c5f5ce59fac2a48faeaee", text: "모든 것이 잘 될 거예요.", model: Typecast::Models::TTS_MODEL_V30, prompt: Typecast::Models::SmartPrompt.new( previous_text: "방금 최고의 소식을 들었어요!", next_text: "축하하고 싶어요!" ) ) ) ``` 프리셋 값으로 감정을 명시적으로 설정합니다: ```ruby response = client.text_to_speech( Typecast::Models::TTSRequest.new( voice_id: "tc_672c5f5ce59fac2a48faeaee", text: "이 기능들을 보여드리게 되어 정말 기쁩니다!", model: Typecast::Models::TTS_MODEL_V30, prompt: Typecast::Models::PresetPrompt.new( emotion_preset: "happy", emotion_intensity: 1.5 ) ) ) ``` ### 오디오 커스터마이징 음량, 피치, 템포, 출력 포맷을 제어합니다: ```ruby response = client.text_to_speech( Typecast::Models::TTSRequest.new( voice_id: "tc_672c5f5ce59fac2a48faeaee", text: "커스터마이징된 오디오 출력!", model: Typecast::Models::TTS_MODEL_V30, output: Typecast::Models::Output.new( target_lufs: -14.0, audio_pitch: 2, audio_tempo: 1.2, audio_format: Typecast::Models::AUDIO_MP3 ), seed: 42 ) ) File.binwrite("output.mp3", response.audio_data) ``` ### 파일로 바로 생성하기 `generate_to_file`은 음성 합성과 파일 저장을 한 번에 처리합니다. `model`은 기본값으로 `ssfm-v30`을 사용하고, `.mp3` 또는 `.wav` 확장자로 출력 형식을 결정합니다. ```ruby client.generate_to_file( "hello.mp3", text: "안녕하세요, 타입캐스트입니다.", voice_id: "tc_672c5f5ce59fac2a48faeaee" # voice_id는 https://studio.typecast.ai/developers/api/voices 에서 확인하세요. ) ``` ### 텍스트만으로 쉼 표현 한 voice로 읽는 문장 안에 쉼만 넣고 싶다면 텍스트에 pause markup을 직접 작성합니다. `<|5s|>`, `<|1s|>`, `<|0.3s|>`, `<|0.34413s|>`처럼 쓰며 값은 초 단위이고 반드시 `s`로 끝납니다. 별도 pause 함수를 호출하지 않아도 텍스트만 보고 쉼 위치를 확인할 수 있습니다. ```ruby audio = client .compose_speech .defaults(voice_id: "tc_672c5f5ce59fac2a48faeaee", model: "ssfm-v30") .say("안녕하세요<|5s|>반갑습니다<|1s|>오늘<|2s|>날씨는 어떤 것 같으세요?") .generate ``` ### 다중 화자 합성 한 파일 안에서 서로 다른 voice나 구간별 pitch, tempo, prompt, seed 같은 옵션을 조합해야 할 때 사용합니다. composer는 세그먼트를 `POST /v1/text-to-speech/compose`로 보내며 WAV 또는 MP3를 직접 반환합니다. 무음 제거는 TTS 세그먼트에 명시적으로 설정하고, 명시적인 쉼은 유지됩니다. ```ruby audio = client .compose_speech .defaults(voice_id: "tc_672c5f5ce59fac2a48faeaee", model: Typecast::Models::TTS_MODEL_V30) .say("Hello there") .pause(5) .say("Nice to meet you", voice_id: "tc_60e5426de8b95f1d3000d7b5", output: { audio_pitch: 2 }) .say("Today") .pause(2) .say("How does the weather feel?") .generate File.binwrite("conversation.wav", audio.audio_data) ``` ### 보이스 탐색 (V3 API) 향상된 메타데이터와 함께 사용 가능한 보이스를 조회합니다: ```ruby voices = client.get_voices_v3 filtered = client.get_voices_v3( Typecast::Models::VoicesV2Filter.new( model: Typecast::Models::TTS_MODEL_V30, gender: "female", age: "young_adult" ) ) voices.each do |voice| puts "ID: #{voice.voice_id}, 이름: #{voice.voice_name.kor}" puts "성별: #{voice.gender}, 나이: #{voice.age}" end voice = client.get_voice_v3("tc_672c5f5ce59fac2a48faeaee") puts voice.voice_name.kor ``` ### 스트리밍 `text_to_speech_stream()`으로 스트리밍 엔드포인트를 호출합니다: ```ruby client.text_to_speech_stream( Typecast::Models::TTSRequestStream.new( voice_id: "tc_672c5f5ce59fac2a48faeaee", text: "이 텍스트를 오디오로 스트리밍합니다.", model: Typecast::Models::TTS_MODEL_V30, output: Typecast::Models::OutputStream.new(audio_format: "wav") ) ) do |audio| File.binwrite("stream.wav", audio) end ``` **WAV 스트리밍 형식:** 32000 Hz, 16비트, 모노 PCM. 첫 번째 청크에 44바이트 WAV 헤더(size = `0xFFFFFFFF`)가 포함되며, 이후 청크는 원시 PCM 데이터만 포함합니다. ## 타임스탬프 TTS `text_to_speech_with_timestamps()`는 `POST /v1/text-to-speech/with-timestamps`를 래핑하며, 오디오와 함께 단어·문자 단위 정렬 데이터를 반환합니다. ```ruby result = client.text_to_speech_with_timestamps( Typecast::Models::TTSRequest.new( voice_id: "tc_60e5426de8b95f1d3000d7b5", text: "안녕하세요. 반갑습니다.", model: Typecast::Models::TTS_MODEL_V30, language: "kor" ) ) result.save_audio("output.wav") puts "재생 시간: #{result.audio_duration}초" result.words.each do |word| puts "[#{word.start_time}초 - #{word.end_time}초] #{word.word}" end ``` ### 정밀도(Granularity) 설정 `granularity: "word"`(기본값) 또는 `granularity: "char"`를 설정해 정렬 단위를 제어합니다. ```ruby result = client.text_to_speech_with_timestamps( Typecast::Models::TTSRequest.new( voice_id: "tc_60e5426de8b95f1d3000d7b5", text: "안녕하세요. 반갑습니다.", model: Typecast::Models::TTS_MODEL_V30, language: "kor" ), granularity: "char" ) ``` ### 자막 내보내기 ```ruby File.write("output.srt", result.to_srt) File.write("output.vtt", result.to_vtt) ``` ## 즉시 보이스 클로닝 짧은 WAV 샘플을 업로드해 커스텀 보이스를 생성합니다: ```ruby voice = client.clone_voice( audio: File.binread("sample.wav"), filename: "sample.wav", name: "My Voice", model: Typecast::Models::TTS_MODEL_V30 ) puts "커스텀 보이스 ID: #{voice.voice_id}" ``` 보이스 클로닝 오디오는 **25 MB 이하**여야 하며, 커스텀 보이스 이름은 **1-30자**여야 합니다. ## 무음 길이 조절 이 기능은 **0.1.11 이상**에서 지원합니다. `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. 일반·스트리밍·타임스탬프 TTS는 `output.remove_silence_ms`, Compose는 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 전달합니다. 반환 타임스탬프는 처리 후 오디오를 기준으로 하며, 명시적인 `pause` 세그먼트는 유지됩니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. 아래 출력 설정을 해당 요청의 `output`에 전달하세요. 스트리밍은 스트리밍 전용 출력 타입을 사용합니다. ```ruby output = Typecast::Models::Output.new(remove_silence_ms: 300) stream_output = Typecast::Models::OutputStream.new(remove_silence_ms: 300) ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Autotag SDK ## 개요 **타입캐스트 오토태그**는 문장 속에 포함된 전화번호, 날짜, 시간, 금액 같은 복잡한 숫자나 기호를 TTS가 사람처럼 자연스럽게 읽을 수 있도록 미리 문장을 다듬어주는 텍스트 전처리 SDK입니다. @neosapience/typecast-autotag typecast-autotag com.neosapience:typecast-autotag 소스, 이슈, 네이티브 바이너리 Python 패키지 **typecast-autotag 2.0.0**은 Python 3.10 이상이 필요하며 검증된 범위는 3.10~3.13입니다. **Python 3.8, 3.9는 EOL로 인해 지원이 중단되었습니다.** 해당 버전들을 마지막으로 지원하는 Autotag Python 패키지는 **1.13.0**입니다. Python을 업그레이드한 뒤 `python -m pip install --upgrade typecast-autotag`를 실행하세요. 이전 환경을 임시 유지하려면 `python -m pip install "typecast-autotag==1.13.0"`로 고정할 수 있지만 EOL 보안 지원은 복구되지 않습니다. 이 변경은 Python 패키지에 해당하며 JavaScript·Java 패키지 버전과 구분합니다. ## AutoTag를 사용해야 하는 이유 음성 애플리케이션을 구축할 때, 원본 텍스트는 자연스러운 음성으로 변환되기 어렵습니다: | 입력 | AutoTag 없이 | AutoTag 사용 | |------|-------------|--------------| | `010-1234-5678` | "영 일 영 다시 일 이 삼 사 다시 오 육 칠 팔" | "공 . 일 . 공, 일 . 이 . 삼 . 사, 오 . 육 . 칠 . 팔" | | `50000원` | "오만영원" | "오만원" | | `14:30` | "십사콜론삼십" | "오후 두시 삼십분" | AutoTag는 이러한 패턴을 자동으로 감지하고 자연스러운 음성으로 변환하여 음성 애플리케이션의 사용자 경험을 향상시킵니다. ## 언어 지원 JavaScript·브라우저 패키지는 **SSFM v3.0 TTS 전체 언어**를 지원합니다. | 언어 단계 | 지원 범위 | 공식 코드 / 허용 별칭 | | --- | --- | --- | | 한국어·영어 | 전체 패턴 | `ko`, `kor`, `en`, `eng` | | 일본어·중국어 간체 | 핵심 TTS 패턴 | `ja`, `jpn`, `zh`, `zho` | | 번체 한자권 음성 | 핵심 TTS 패턴 | `zh-TW`, `nan`, `yue` | | 그 외 SSFM v3.0 언어 | 공통 TTS 패턴(31개) | 아래 공식 ISO 639-3 코드 | SSFM v3.0 공식 언어 코드(37개): `ara`, `ben`, `bul`, `ces`, `dan`, `deu`, `ell`, `eng`, `fin`, `fra`, `hin`, `hrv`, `hun`, `ind`, `ita`, `jpn`, `kor`, `msa`, `nan`, `nld`, `nor`, `pan`, `pol`, `por`, `ron`, `rus`, `slk`, `spa`, `swe`, `tam`, `tgl`, `tha`, `tur`, `ukr`, `vie`, `yue`, `zho`. 추가로 허용하는 5개 값은 별도 공식 언어가 아닌 별칭 또는 로케일 태그입니다: `ko` → `kor`, `en` → `eng`, `ja` → `jpn`, `zh` → `zho`, `zh-TW` → 번체 중국어. 전용 규칙 모듈이 없는 31개 언어는 `datetime`, `date`, `time`, `money`, `phone`, `percentage`, `range`, `unit`, `serial`, `number` 패턴을 처리합니다. 로케일별 날짜 순서·월 이름·통화명·소수 구분자·12/24시간제와 주요 고유 숫자 문자를 적용합니다. `nan`과 `yue`는 각 TTS 음성 코드를 유지하면서 번체 중국어 패턴 파이프라인을 재사용합니다. 자연스러운 숫자 읽기, 날짜/시간 포맷팅 등을 포함한 한국어 텍스트 전처리를 완벽하게 지원합니다. ```typescript import { autoTag } from '@neosapience/typecast-autotag'; autoTag('전화번호는 010-1234-5678입니다.', { language: 'ko' }); // → '전화번호는, 공 . 일 . 공, 일 . 이 . 삼 . 사, 오 . 육 . 칠 . 팔 입니다.' autoTag('총 금액은 50000원입니다.', { language: 'ko' }); // → '총 금액은 오만원 입니다.' ``` 적절한 숫자 읽기, 통화 포맷팅 등을 포함한 영어 텍스트 전처리를 완벽하게 지원합니다. ```typescript import { autoTag } from '@neosapience/typecast-autotag'; autoTag('Call me at 555-123-4567.', { language: 'en' }); // → 'Call me at five five five one two three four five six seven.' autoTag('Total is $1,500.', { language: 'en' }); // → 'Total is one thousand five hundred dollars.' ``` 불규칙 시각·카운터 읽기, 문맥형 식별자, 성경 구절을 포함한 일본어 핵심 TTS 패턴을 지원합니다. ```typescript import { autoTag } from '@neosapience/typecast-autotag'; autoTag('受付は14時から19時まで、全部で6件です。', { language: 'ja' }); // → '受付はじゅうよじからじゅうくじまで、全部でろっけんです。' autoTag('注文番号はZX-407、ヨハネ3:16を確認してください。', { language: 'ja' }); // → '注文番号はZ・X、よん・ゼロ・なな、ヨハネさんしょうじゅうろくせつを確認してください。' ``` 식별자, 항공편, 단위, 성경 구절을 포함한 중국어 간체 핵심 TTS 패턴을 지원합니다. ```typescript import { autoTag } from '@neosapience/typecast-autotag'; autoTag('订单编号是ZX-407,请读约翰福音3:16。', { language: 'zh' }); // → '订单编号是Z·X、四·零·七,请读约翰福音三章十六节。' ``` 번체 입력과 대만 전화번호·우편번호·통화·측정 단위·식별자 읽기를 지원합니다. ```typescript import { autoTag } from '@neosapience/typecast-autotag'; autoTag('客服時間是6–9點,費用是NT$12,800。', { language: 'zh-TW' }); // → '客服時間是六點到九點,費用是一萬二千八百新臺幣。' autoTag('訂單編號是ZX-407,請讀約翰福音3:16。', { language: 'zh-TW' }); // → '訂單編號是Z·X、四·零·七,請讀約翰福音三章十六節。' ``` 요청한 언어의 로케일에 맞는 숫자 읽기로 공통 패턴을 변환합니다. ```typescript import { autoTag } from '@neosapience/typecast-autotag'; autoTag('Total 1,234.5 and 72.5%.', { language: 'spa' }); // → 'Total mil doscientos treinta y cuatro punto cinco and setenta y dos punto cinco%.' ``` 공식 언어 코드 37개는 JavaScript/TypeScript 및 브라우저 패키지에서 제공합니다. Python·Java·C/C++는 현재 한국어·영어 진입점을 제공합니다. ## 설치 공개 npm 패키지를 설치합니다: ```bash pnpm add @neosapience/typecast-autotag # 또는 npm install @neosapience/typecast-autotag yarn add @neosapience/typecast-autotag ``` ```typescript import { autoTag } from '@neosapience/typecast-autotag'; autoTag('전화번호는 010-1234-5678입니다.', { language: 'ko' }); ``` 공개 PyPI 패키지를 설치합니다: ```bash pip install typecast-autotag ``` ```python from typecast_autotag import auto_tag, auto_tag_en # 한국어 result = auto_tag('전화번호는 010-1234-5678입니다.') # → '전화번호는, 공 . 일 . 공, 일 . 이 . 삼 . 사, 오 . 육 . 칠 . 팔 입니다.' # 영어 result = auto_tag_en('Call 555-123-4567.') # → 'Call five five five, one two three, four five six seven.' ``` Maven Central 아티팩트를 프로젝트에 추가합니다: ```xml com.neosapience typecast-autotag 1.13.0 ``` ```gradle implementation "com.neosapience:typecast-autotag:1.13.0" ``` ```java import ai.typecast.autotag.TypecastAutotag; // 한국어 String result = TypecastAutotag.autoTag("전화번호는 010-1234-5678입니다.", "ko"); // → "전화번호는, 공 . 일 . 공, 일 . 이 . 삼 . 사, 오 . 육 . 칠 . 팔 입니다." // 영어 String result = TypecastAutotag.autoTag("Call 555-123-4567.", "en"); // → "Call five five five, one two three, four five six seven." ``` [GitHub Releases](https://github.com/neosapience/typecast-autotag/releases) 에서 미리 빌드된 네이티브 바이너리를 받거나 소스에서 빌드하세요. ```bash git clone https://github.com/neosapience/typecast-autotag.git cd typecast-autotag pnpm install pnpm c-binding:build-all-multiarch # 헤더와 라이브러리가 c-binding/build/ 아래에 떨어집니다. ``` ```c #include "typecast_autotag.h" char* result = typecast_auto_tag("전화번호는 010-1234-5678입니다."); // → "전화번호는, 공 . 일 . 공, 일 . 이 . 삼 . 사, 오 . 육 . 칠 . 팔 입니다." typecast_free(result); ``` ## 빠른 시작 ### 자동 태깅 텍스트에서 패턴을 자동으로 감지하고 변환합니다: ```typescript import { autoTag } from '@neosapience/typecast-autotag'; // 전화번호 autoTag('전화번호는 010-1234-5678입니다.', { language: 'ko' }); // → '전화번호는, 공 . 일 . 공, 일 . 이 . 삼 . 사, 오 . 육 . 칠 . 팔 입니다.' // 날짜와 시간 autoTag('회의는 14:30에 시작합니다.', { language: 'ko' }); // → '회의는 오후 두시 삼십분 에 시작합니다.' // 금액 autoTag('총 금액은 50000원입니다.', { language: 'ko' }); // → '총 금액은 오만원 입니다.' ``` ### 수동 태깅 명시적인 태그 구문을 사용하여 정밀하게 제어합니다: ```typescript import { manualTag } from '@neosapience/typecast-autotag'; // 이름 철자 읽기 (글자별) manualTag('안녕하세요, name(김철수)님.', { language: 'ko' }); // → '안녕하세요, 김 . 철 . 수님.' manualTag('Your code is digits(2048).', { language: 'en' }); // → 'Your code is two zero four eight.' ``` ### 조합 사용 자동 태그와 수동 태그를 함께 적용합니다: ```typescript import { autoTagWithManual } from '@neosapience/typecast-autotag'; autoTagWithManual('name(김철수)님, 010-1234-5678로 연락주세요.', { language: 'ko' }); // → '김 . 철 . 수님, 공 . 일 . 공, 일 . 이 . 삼 . 사, 오 . 육 . 칠 . 팔 로 연락주세요.' ``` 수동 태그가 먼저 처리된 후 나머지 텍스트에 자동 태그가 적용됩니다. ## 지원 태그 언어별 지원 태그는 다릅니다. 정확한 런타임 목록은 `getSupportedAutoTags(language)`로 확인할 수 있습니다. 일본어·중국어 간체·대만 Mandarin은 지역 우편번호, 범위, 점수, 분수, 단위, 이메일 기호, 방향, 문맥형 일련번호·계좌·항공편 식별자, 성경 구절도 자동으로 인식합니다. ### 자동 태그 (자동 감지) | 태그 | 설명 | 한국어 예시 | 영어 예시 | |------|------|-------------|-----------| | `phone` | 전화번호 | `010-1234-5678` | `555-123-4567` | | `datetime` | 날짜와 시간 | `2024-01-15T14:30` | `2024-01-15T14:30` | | `time` | 시간 | `14:30` | `2:30 PM` | | `date` | 날짜 | `2024-01-15` | `January 15, 2024` | | `money` | 금액 | `50000원` | `$1,500` | | `year` | 연도 | `2024년` | `year 2024` | | `month` | 월 | `12월` | `January` | | `day` | 일 | `25일` | `the 15th` | | `order` | 순서 | `3번째` | `1st place` | | `point` | 점수 | `95점` | `95 points` | | `ratio` | 비율/퍼센트 | `50%`, `1:2` | `50%`, `1:2` | | `weight` | 무게 | `5kg` | `5kg`, `100lb` | | `distance` | 거리 | `5km` | `5km`, `100m` | | `temperature` | 온도 | `25℃` | `25°C`, `-5°F` | | `volume` | 용량 | `500ml` | `500ml`, `2L` | | `dataCapacity` | 데이터 용량 | `100GB` | `100GB`, `50Mbps` | ### 수동 전용 태그 | 태그 | 설명 | 구문 | 출력 | |------|------|------|------| | `name` | 이름 (글자별) | `name(김철수)` | `김 . 철 . 수` | | `digits` | 숫자 (자리별) | `digits(1234)` | `일 . 이 . 삼 . 사` | | `address` | 주소 (한국어 전용) | `address(102동 1101호)` | `백이동 천백일호` | ## AICC 사용 사례 자연스러운 음성이 중요한 AI 컨택센터 애플리케이션에 완벽합니다: ```typescript import { autoTagWithManual } from '@neosapience/typecast-autotag'; // 고객 서비스 스크립트 const customerName = '김철수'; const orderNumber = '1234'; const deliveryDate = '2024년 1월 15일'; const supportPhone = '010-1234-5678'; const script = autoTagWithManual(` 안녕하세요, name(${customerName})님. 주문번호 ${orderNumber} 상품이 ${deliveryDate}에 배송될 예정입니다. 문의사항은 ${supportPhone}으로 연락주세요. `, { language: 'ko' }); // 출력: // "안녕하세요, 김 . 철 . 수님. // 주문번호 천이백삼십사 상품이 이천이십사년 일월 십오일 에 배송될 예정입니다. // 문의사항은, 공 . 일 . 공, 일 . 이 . 삼 . 사, 오 . 육 . 칠 . 팔 으로 연락주세요." ``` ## Typecast TTS와 연동 AutoTag를 Typecast TTS API와 결합하여 최상의 음성 경험을 제공하세요: ```typescript import { autoTagWithManual } from '@neosapience/typecast-autotag'; import { TypecastClient } from '@neosapience/typecast-js'; const client = new TypecastClient({ apiKey: 'YOUR_API_KEY' }); // AutoTag로 원본 텍스트 전처리 const rawText = '잔액은 1,234,567원입니다. 문의는 1588-1234로 전화주세요.'; const processedText = autoTagWithManual(rawText, { language: 'ko' }); // Typecast TTS로 전송 const audio = await client.textToSpeech({ text: processedText, model: 'ssfm-v30', voice_id: 'tc_672c5f5ce59fac2a48faeaee' }); ``` ## 플랫폼 지원 ### 개발 언어 | 언어 | 버전 | 설치 경로 | 텍스트 언어 | |------|------|-----------|-------------| | Node.js | ≥18 | npm의 `@neosapience/typecast-autotag` | 공식 코드 37개 + `ko`, `en`, `ja`, `zh`, `zh-TW` 별칭 | | Browser | Modern | `@neosapience/typecast-autotag` ESM/UMD 번들 | 공식 코드 37개 + `ko`, `en`, `ja`, `zh`, `zh-TW` 별칭 | | Python | ≥3.8 | PyPI의 `typecast-autotag` | `ko`, `en` | | Java | ≥8 | Maven Central의 `com.neosapience:typecast-autotag` | `ko`, `en` | | C/C++ | Any | Releases의 미리 빌드된 바이너리 또는 `pnpm c-binding:build-all-multiarch` | `ko`, `en` | ### 서버 플랫폼 | 플랫폼 | 상태 | |--------|------| | Linux | 지원 (CentOS 6.9+, Amazon Linux 2+, Ubuntu, Debian) | | macOS | 지원 (Intel & Apple Silicon) | | Windows | 지원 (Windows 10+) | ### 아키텍처 | 아키텍처 | 상태 | |----------|------| | x86_64 (AMD64) | 지원 | | x86 (32비트) | 지원 | | arm64 (AArch64) | 지원 | | armv7 (32비트 ARM) | 지원 | ## 다음 단계 Typecast TTS API 시작하기 SDK 문서 살펴보기 --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # AI로 쇼츠 영상 만들기 스킬 파일을 AI 에이전트에게 전달한 다음 대화를 시작하면 에이전트가 영상 제작 과정을 도와줍니다. ## 준비할 것 타입캐스트 API 계정만 있으면 시작할 수 있습니다. ## 1. 에이전트에게 스킬 전달하기 아래 영역을 열어 스킬 원문 전체를 복사한 다음 AI 에이전트에게 전달하세요. 별도 저장소에 접근하지 않아도 이 내용만으로 실행할 수 있습니다. ````markdown --- name: create-typecast-shorts description: Create a captioned 9:16 short or reel from one rights-cleared local video or image using Typecast narration, timestamp output, and ffmpeg. Use when a user asks to make a short-form video, reel, vertical video, narrated social clip, or Typecast-powered short from media they own or are authorized to use. --- # Create Typecast Shorts Create one 1080×1920 MP4 from a local video or image, Typecast narration, and timestamp-aligned subtitles. ## Boundaries - Accept only an attached file or local file the user owns or is authorized to use. - If rights are unclear, ask the user to confirm them before processing. - Do not search for, download, scrape, or reuse third-party video, news, or social media. - Do not remove logos, watermarks, attribution, or embedded subtitles to conceal a source. - Do not upload or publish the result. Return local artifacts for user review. - Do not print, log, or place a Typecast API key in chat, commands, scripts, or output files. - Support one background video or image per run. Ask the user to choose one when several are provided. ## Workflow ### 1. Confirm inputs Collect: - Local media path - Topic or finished script - Language - Target duration; default to 45–60 seconds - Typecast voice ID, or permission to open the interactive voice picker - Output directory inside the current workspace Use these defaults unless the user specifies otherwise: - 1080×1920, 30 fps - Center crop to fill the frame - Discard source audio - White bottom-centered subtitles with a black outline Confirm media rights, the final script, and the voice before making the paid TTS request. ### 2. Check the environment Run: ```bash command -v cast command -v ffmpeg command -v ffprobe cast --help ffmpeg -filters 2>/dev/null | grep subtitles ``` If a command or the ffmpeg `subtitles` filter is missing, explain the missing dependency. Install it only with user approval. After installing cast with Go, add it to the current shell without hardcoding the user's home directory: ```bash export PATH="$(go env GOPATH)/bin:$PATH" ``` For cast installation, use the official options: ```bash brew install neosapience/tap/cast # or go install github.com/neosapience/cast@latest ``` Authenticate with `cast login` so the key is entered in its own prompt. Never ask the user to paste the key into chat. ### 3. Create a clean work directory Create a new, explicit directory inside the current workspace. Do not overwrite an existing output. Keep these artifacts: ```text script.txt script-tts.txt narration.wav captions.srt preview.mp4 final.mp4 ``` Work from this directory while rendering so `captions.srt` does not require platform-specific path escaping. ### 4. Prepare the script If the user supplied only a topic, draft a concise script with: 1. Hook 2. Main point 3. Supporting detail 4. Closing line Save the approved text as `script.txt`. Create `script-tts.txt` separately. Change only pronunciations that TTS may misread, such as numbers, abbreviations, URLs, symbols, or mixed-language terms. Preserve the meaning and never overwrite `script.txt`. ### 5. Select a voice and generate audio plus captions If no voice ID was provided, run: ```bash cast voices pick ``` After approval, generate narration and SRT together: ```bash cast "$(cat script-tts.txt)" \ --voice-id VOICE_ID \ --language LANGUAGE_CODE \ --format wav \ --out narration.wav \ --timestamp-out captions.srt \ --timestamp-format srt ``` Use ISO 639-3 language codes such as `kor`, `eng`, or `jpn`. For Japanese or Chinese, cast automatically selects character-level alignment; use the latest cast release if that behavior is unavailable. Do not fall back to Whisper merely to create timestamps. Typecast captions already returns aligned audio and subtitles without another model or dependency. ### 6. Inspect the source Run: ```bash ffprobe -v error -show_entries stream=codec_type,width,height,duration \ -of default=noprint_wrappers=1 "SOURCE_PATH" ``` Use the video command for a video source and the image command for a still image. Quote every user-provided path. ### 7. Render a 15-second preview For a video: ```bash ffmpeg -y -stream_loop -1 -i "SOURCE_PATH" -i narration.wav \ -filter_complex "[0:v]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,setsar=1,subtitles=captions.srt:force_style='FontSize=16,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,BorderStyle=1,Outline=1.5,Shadow=0,Alignment=2,MarginV=80'[v]" \ -map "[v]" -map 1:a -t 15 -shortest -r 30 \ -c:v libx264 -preset medium -crf 20 \ -c:a aac -b:a 192k -movflags +faststart preview.mp4 ``` For an image: ```bash ffmpeg -y -loop 1 -framerate 30 -i "SOURCE_PATH" -i narration.wav \ -filter_complex "[0:v]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,setsar=1,subtitles=captions.srt:force_style='FontSize=16,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,BorderStyle=1,Outline=1.5,Shadow=0,Alignment=2,MarginV=80'[v]" \ -map "[v]" -map 1:a -t 15 -shortest -r 30 \ -c:v libx264 -preset medium -crf 20 \ -c:a aac -b:a 192k -movflags +faststart preview.mp4 ``` Show the preview to the user. Check framing, subtitle readability, pronunciation, and timing before the full render. If center crop cuts off important content, replace the scale and crop portion with: ```text scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:black ``` ### 8. Render and verify the final video After preview approval, rerun the matching command without `-t 15`, add `-shortest`, and write `final.mp4`. Verify: ```bash ffprobe -v error \ -show_entries stream=codec_name,width,height -show_entries format=duration,size \ -of default=noprint_wrappers=1 final.mp4 ``` Require: - 1080×1920 video - H.264 video and AAC audio - Non-zero duration and size - Narration and subtitles ending with the video Return the output directory and artifact list. Remind the user to review the complete video before publishing it themselves. ## Failure handling - For 401 or 403 from cast, re-run `cast login` and verify the Global API plan without exposing the key. - For 402 or 429, report the billing or rate-limit response; do not retry repeatedly. - If timestamp flags are unavailable, update cast instead of adding a parallel transcription stack. - If ffmpeg cannot load subtitles, use an ffmpeg build with libass. - If rendering fails, preserve all existing artifacts and rerun only the failed step. ```` ## 2. 에이전트와 대화하며 만들기 원하는 쇼츠를 편하게 설명한 다음, 아래 항목을 대화 중 자유롭게 바꿔보세요. | 바꿀 수 있는 것 | 이렇게 말해보세요 | | --------- | ------------------------------------ | | 대본과 메시지 | “도입부를 더 강하게 해줘”, “핵심 내용을 하나로 줄여줘” | | 목소리와 말투 | “더 밝고 활기찬 목소리로 바꿔줘”, “차분하게 읽어줘” | | 길이와 속도 | “30초 안으로 줄여줘”, “조금 더 빠르게 진행해줘” | | 자막과 화면 | “자막을 더 짧게 나눠줘”, “중요한 장면이 잘 보이게 조정해줘” | 마음에 들 때까지 에이전트와 대화하며 바로바로 다듬을 수 있습니다. 미디어 업로드 시 사용한 저작권 출처를 꼭 명시하세요. ## 에이전트가 처리하는 작업 승인을 받은 다음 cast CLI와 ffmpeg를 확인하고 설치합니다. 대본을 정리하고 타입캐스트 내레이션을 안전하게 생성합니다. 같은 내레이션에서 타임스탬프 기반 SRT 자막을 생성합니다. 검수용 미리보기와 최종 9:16 MP4를 렌더링합니다. 사용자는 미디어, 주제, 단계별 승인만 제공하면 됩니다. 명령줄 도구 설치와 렌더링은 에이전트에게 맡기세요. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 오픈소스 프로젝트 예시 커뮤니티 프로젝트를 활용하면 타입캐스트 TTS를 쇼츠, 영상, 더빙, 내레이션, 음성 자산 제작, 에이전트 프레임워크, 개발 도구에 연결할 수 있습니다. 아래 연동은 외부 개발자가 만들고 유지보수합니다. 아래 프로젝트는 타입캐스트가 직접 유지보수하거나 보증하는 제품이 아닙니다. 프로덕션에 적용하기 전에 각 저장소의 문서, 라이선스, 보안 방식, 현재 API 호환성을 확인하세요. 링크는 2026년 8월 14일에 마지막으로 검토했습니다. ## 콘텐츠와 영상 제작 쇼츠, 영상 내레이션, 자막, 캐릭터 음성, 미디어 자동화 워크플로우를 만들고 있다면 이 프로젝트부터 살펴보세요. 블로그 발행을 자동화하고 글을 숏폼 영상으로 재가공하며, 타입캐스트 내레이션과 Whisper 기반 단어 타임스탬프를 사용합니다. 타입캐스트 보이스, 감정, 라우드니스 조절을 AI 영상 생성과 CapCut, Premiere, Vrew 내보내기 흐름에 연결합니다. 타임스탬프 TTS를 사용해 대본에서 장면, 자막, 다중 보이스 내레이션, CapCut 초안을 생성합니다. 간결한 명령줄 워크플로우로 내레이션 대본에서 컷별 MP3와 SRT 자막을 생성합니다. Instagram, TikTok, Pinterest 제휴 숏폼 자동 제작 과정에 타입캐스트 내레이션을 추가합니다. 영상 제작 모노레포에서 MCP 도구로 보이스를 조회하고 타임스탬프 음성을 생성해 파일로 저장합니다. 업로드한 사진으로 블로그 글과 숏폼 영상을 만들고, 각 영상 장면 길이에 맞춘 타입캐스트 내레이션을 사용합니다. 타입캐스트에 집중한 작은 도구로 YAML 매니페스트에서 캐릭터 반응 음성을 일괄 생성합니다. ## 에이전트와 실시간 음성 대화형 에이전트, 실시간 음성 엔진, API 호환 계층, 음성 모델 평가 도구에 타입캐스트를 적용한 프로젝트입니다. LlamaIndex 에이전트에 음성 생성, 보이스 목록 조회, 보이스 상세 조회 도구를 추가합니다. TEN Framework로 만든 대화형 실시간 음성 에이전트에서 타입캐스트 스트리밍 TTS를 사용합니다. 보이스 조회, 감정 조절, PyAudio 스트리밍을 지원하는 Python 실시간 TTS 엔진으로 타입캐스트를 사용합니다. 음성 인식, LLM, 타입캐스트 음성 합성을 연결한 로컬 음성 어시스턴트 구현을 확인합니다. OpenAI 호환 TTS 클라이언트의 요청을 멀티 프로바이더 백엔드를 통해 타입캐스트로 전달합니다. 이 저장소는 보관 처리되어 더 이상 유지보수되지 않습니다. 크라우드소싱 블라인드 평가 환경에서 타입캐스트 SSFM 3.0과 다른 음성 모델을 비교합니다. 비전, 사용자 기억, Gemini 대화를 결합한 멀티모달 로봇에서 타입캐스트를 선택형 비동기 음성 엔진으로 사용합니다. 간결한 JavaScript 봇 예제를 통해 Discord에서 타입캐스트 음성을 생성하고 재생합니다. ## 프로젝트를 사용하기 전에 * 현재 타입캐스트 API 또는 유지보수 중인 타입캐스트 SDK를 사용하는지 확인하세요. * API 키는 서버나 환경 변수에 보관하고 저장소에 커밋하지 마세요. * 코드를 복사하거나 배포하기 전에 프로젝트 라이선스를 확인하세요. 이 목록에는 라이선스를 명시하지 않은 프로젝트도 포함되어 있습니다. * 보이스, 모델, 스트리밍, 타임스탬프 동작과 호환 버전을 자신의 환경에서 테스트하세요. 타입캐스트 TTS로 유용한 프로젝트를 만들었다면 명확한 설치 안내, 라이선스, 타입캐스트 연동 경로를 포함한 공개 저장소를 공유해 주세요. 커뮤니티가 프로젝트를 검토하고 재사용하는 데 도움이 됩니다. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # OpenClaw [OpenClaw](https://docs.openclaw.ai/)는 도구, 셸 명령, MCP 서버를 실행할 수 있는 AI 에이전트 런타임입니다. 타입캐스트와 함께 사용하면 에이전트가 단일 명령으로 자연스러운 음성을 생성할 수 있습니다. ## 할 수 있는 것 타입캐스트와 OpenClaw를 사용하면 다음을 할 수 있습니다: - **에이전트 워크플로우에서 음성 생성** - 모든 텍스트 출력을 자연스러운 오디오로 변환 - **600+개 보이스 선택** - 성별, 나이, 스타일별로 보이스 선택 - **감정 제어** - 스마트 이모션 또는 이모션 프리셋(happy, sad, angry, whisper 등) 적용 - **37개 언어 지원** - 영어, 한국어, 일본어, 중국어 등으로 음성 생성 - **오디오 파이프라인 자동화** - 다른 도구와 결합하여 엔드투엔드 콘텐츠 제작 --- ## 사전 요구 사항 시작하기 전에 다음을 준비하세요: 1. **OpenClaw** 설치 - `npm install -g openclaw@latest` 2. **타입캐스트 API 키** - [여기서 받기](https://studio.typecast.ai/developers/api/) 3. **타입캐스트 CLI (`cast`)** - 가장 빠른 연동 방법 --- ## 빠른 시작: cast CLI 공식 타입캐스트 CLI는 음성 생성을 단일 셸 명령으로 처리합니다. 에이전트가 셸 명령을 실행할 수 있다면 커스텀 프로바이더 없이 타입캐스트 오디오를 생성할 수 있습니다. ### 단계 1: CLI 설치 ```bash brew install neosapience/tap/cast ``` ```bash go install github.com/neosapience/cast@latest ``` ### 단계 2: 인증 ```bash cast login ``` 또는 키를 직접 전달: ```bash cast login ``` ### 단계 3: 확인 ```bash cast "안녕하세요!" --out ./test.mp3 --format mp3 ``` 파일이 정상적으로 생성되면 OpenClaw와 함께 사용할 준비가 된 것입니다. --- ## 연동 방법 ### 방법 1: cast를 통한 Local exec (권장) OpenClaw는 local `exec`과 remote `code_execution`을 구분합니다. 설치된 바이너리에 접근해야 할 때는 local `exec`을 사용하세요. OpenClaw 에이전트에게 다음과 같이 요청하세요: ```text local exec으로 다음 명령을 실행해줘: cast "금요일 오후 7시로 예약이 확정되었습니다." --language kor --format mp3 --out ./confirmation.mp3 생성된 파일 경로를 반환해줘. ``` 반복적으로 사용하려면 OpenClaw 설정에 프로젝트 인스트럭션을 추가하세요: ```markdown 사용자가 음성 오디오를 요청하면 로컬 `cast` CLI를 사용하세요. 기본 명령: cast "$TEXT" --voice-id "$TYPECAST_VOICE_ID" --language "${TYPECAST_LANGUAGE:-kor}" --format "${TYPECAST_FORMAT:-mp3}" --out "$OUTPUT" API 키를 출력하지 마세요. 헤드리스 세션에서는 `--out`을 사용하세요. ``` **권장 환경 변수:** ```bash export TYPECAST_VOICE_ID="tc_60e5426de8b95f1d3000d7b5" export TYPECAST_LANGUAGE="kor" export TYPECAST_FORMAT="mp3" ``` ### 방법 2: MCP 서버 (도구 네이티브) 더 깊은 연동을 위해 타입캐스트 API MCP 서버를 연결하여 OpenClaw가 TTS 도구를 직접 호출하도록 하세요. ```bash openclaw mcp set typecast '{ "command": "uvx", "args": [ "--from", "git+https://github.com/neosapience/typecast-api-mcp-server.git", "typecast-api-mcp-server" ], "env": { "TYPECAST_API_KEY": "${TYPECAST_API_KEY}", "TYPECAST_OUTPUT_DIR": "./typecast_output" } }' ``` 확인: ```bash openclaw mcp show typecast ``` OpenClaw 플러그인 번들에 추가: ```json { "mcp": { "servers": { "typecast": { "command": "uvx", "args": [ "--from", "git+https://github.com/neosapience/typecast-api-mcp-server.git", "typecast-api-mcp-server" ], "env": { "TYPECAST_API_KEY": "${TYPECAST_API_KEY}", "TYPECAST_OUTPUT_DIR": "./typecast_output" }, "connectionTimeoutMs": 30000 } } } } ``` 등록 후 타입캐스트 도구가 `typecast__synthesize_speech`, `typecast__list_voices` 등으로 나타납니다. 에이전트에게 요청: ```text typecast MCP 도구를 사용해서 "안녕하세요"를 mp3 파일로 합성해줘. ``` `https://typecast.ai/docs/mcp`에 있는 원격 문서 MCP를 연결하면 오디오를 생성하지 않고도 연동 가이드를 MCP 리소스로 제공받을 수 있습니다. --- ## 보이스 및 감정 제어 ### 보이스 찾기 `cast` CLI로 사용 가능한 보이스를 조회하세요: ```bash cast voices --model ssfm-v30 ``` 또는 에이전트에게 MCP `list_voices` 도구를 사용하여 성별, 나이, 사용 사례별로 검색하도록 요청하세요. ### 감정 옵션 AI가 텍스트 맥락에서 최적의 감정을 자동으로 감지합니다. 자연스러운 대화와 스토리텔링에 적합합니다. 7가지 감정 중에서 수동으로 선택합니다: Normal, Happy, Sad, Angry, Whisper, Tone Up, Tone Down. **cast CLI로 감정 적용:** ```bash # 스마트 이모션 (ssfm-v30 전용) cast "정말 믿을 수 없어요!" --model ssfm-v30 --emotion smart --out ./excited.mp3 # 이모션 프리셋 cast "정말 유감입니다." --model ssfm-v30 --emotion sad --out ./sorry.mp3 ``` --- ## 예시 워크플로우 1. OpenClaw가 회의 녹취록을 수신 2. 에이전트가 LLM으로 핵심 내용 요약 3. 에이전트가 `cast`로 오디오 요약 생성 4. 출력 파일을 Slack 또는 Google Drive에 업로드 1. 에이전트가 영어 콘텐츠 수신 2. 한국어, 일본어, 중국어로 번역 3. 각 언어에 대해 타입캐스트 오디오 생성 4. 모든 오디오 파일을 클라우드 스토리지에 저장 1. 빌드 파이프라인이 OpenClaw 에이전트를 트리거 2. 에이전트가 상태 메시지 생성: "빌드 성공" 또는 "빌드 실패" 3. `cast`로 음성 알림 생성 4. 오디오를 팀 Discord 채널에 게시 --- ## 문제 해결 OpenClaw가 도구를 실행하는 동일한 런타임에 CLI를 설치하세요. Homebrew로 설치한 경우 `PATH`에 Homebrew bin 디렉토리가 포함되어 있는지 확인하세요. `cast login`을 실행하거나 `cast login `로 API 키를 직접 전달하세요. [타입캐스트 API 콘솔](https://studio.typecast.ai/developers/api/)에서 확인 가능합니다. 오디오를 재생하는 대신 `--out`으로 파일에 저장하세요. 사용자에게 파일 경로를 반환하세요. 에이전트에게 `exec` 도구 또는 로컬 셸을 사용하도록 명시적으로 요청하세요. 프로젝트 인스트럭션에 이 동작을 명확히 하세요. - `uvx`가 설치되어 있고 `PATH`에 있는지 확인: `command -v uvx` - `TYPECAST_API_KEY` 환경 변수가 설정되어 있는지 확인 - `openclaw mcp show typecast`로 등록 상태 확인 --- ## 리소스 API 키 발급받기 사용 가능한 모든 보이스 둘러보기 타입캐스트 API 탐색하기 타입캐스트 MCP 서버 문서 --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Skills [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview)는 Anthropic의 AI 코딩 어시스턴트입니다. **Typecast Skills**를 사용하면 Claude가 자연어로 요청하기만 해도 TTS를 프로젝트에 통합하도록 도와줄 수 있습니다! ## 할 수 있는 것 Typecast Skills for Claude를 사용하면 다음을 할 수 있습니다: - **단계별 가이드 받기** - Python, JavaScript 또는 cURL로 API 통합하기 - **작동하는 코드 생성** - 사용 사례에 맞게 조정됨 - **오류 문제 해결** - 자세한 설명 및 솔루션 제공 - **요금제 비교** - 비용 계산 - **음성 찾기** - 프로젝트 요구에 맞는 음성 발견 --- ## 사전 요구 사항 시작하기 전에 다음을 준비하세요: 1. **Claude Code** - [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview)에서 공식 설치 가이드를 확인하세요 2. **타입캐스트 API 키** - [여기서 받기](https://studio.typecast.ai/developers/api/) --- ## 설치 Claude Code를 열고 다음 명령을 실행하세요: ```bash npx skills add neosapience/typecast-skills ``` 이게 끝입니다! Typecast Skills를 사용할 준비가 되었습니다. ### 설치 확인 Claude에게 물어보세요: **"타입캐스트 API에 대해 무엇을 도와줄 수 있어?"** 올바르게 설치되었다면 Claude가 자신을 Typecast TTS API 전문 에이전트로 소개할 것입니다. **수동 설치**: 스킬 폴더를 다음 위치에 직접 복사할 수도 있습니다: - 개인 범위: `~/.claude/skills/` - 프로젝트 범위: `.cursor/skills/` --- ## 빠른 시작: 첫 번째 음성 생성 자연어로 Claude에게 물어보세요! 다음은 몇 가지 예시 프롬프트입니다: ### 기본 설정 ```text 시작하기 Typecast TTS API를 어떻게 시작하나요? ``` ```text API 키 타입캐스트 API 키를 어떻게 받나요? ``` ```text 음성 목록 Python에서 사용 가능한 음성 목록을 보여줘. ``` ### 코드 생성 ```text Python 타입캐스트 API를 사용해서 "안녕하세요"를 음성으로 변환하는 Python 코드를 작성해줘. ``` ```text JavaScript 행복한 감정으로 텍스트 음성 변환하는 JavaScript 함수를 만들어줘. ``` ```text cURL ssfm-v30 모델로 음성을 생성하는 cURL 명령어를 줘. ``` ### 오류 문제 해결 ```text 403 오류 타입캐스트 API에서 403 오류가 발생해요. 무엇을 확인해야 하나요? ``` ```text 요청 한도 오류 429 - 요청이 너무 많다고 나와요. 요청 한도가 어떻게 되나요? ``` ```text 음성을 찾을 수 없음 voice_id가 404를 반환해요. 유효한 음성 ID를 어떻게 찾나요? ``` --- ## 예시 대화 ### 시작하기 (코딩 경험 없음) **Claude가 할 것:** 1. API가 무엇인지 간단하게 설명 2. API 키 받는 방법 안내 3. 가장 간단한 코드 예제 제공 4. 각 코드 라인 설명 **Claude가 할 것:** - v30이 더 나은 품질의 최신 모델임을 설명 - 각 모델에서 사용 가능한 감정 나열 - 새 프로젝트에는 v30 권장 - 스마트 모드(문맥 인식 감정) 설명 ### 프로젝트에 통합하기 **Claude가 할 것:** 1. API 호출은 백엔드를 통해야 한다고 설명 (보안) 2. Node.js/Express 백엔드 예제 제공 3. React에서 호출하는 방법 제공 4. 오류 처리 포함 **Claude가 할 것:** 1. 완전한 FastAPI 엔드포인트 생성 2. API 키에 환경 변수 사용 3. 적절한 오류 처리 포함 4. 예시 요청/응답 제공 --- ## Claude가 Typecast에 대해 아는 것 | 주제 | Claude가 도와줄 수 있는 것 | |-------|---------------------------| | **API 기본** | 인증, 엔드포인트, 요청/응답 형식 | | **코드 샘플** | Python SDK, JavaScript SDK, Direct API, cURL | | **음성 선택** | 600+개 음성, 성별/나이/사용 사례별 필터링 | | **감정 제어** | 이모션 프리셋, 스마트 모드, 강도 조절 | | **오디오 설정** | 형식(WAV/MP3), 볼륨, 피치, 템포 | | **오류 처리** | 모든 오류 코드(400-500) 및 해결책 | | **요금제** | 플랜 비교, 크레딧 계산 | | **모범 사례** | 보안, 환경 변수, 요청 한도 | --- ## 최상의 결과를 위한 팁 "TTS 도와줘" 대신 "ssfm-v30으로 슬픈 감정의 Python 코드를 생성해줘"라고 요청하세요 기술 스택을 알려주세요: "Next.js와 TypeScript를 사용하고 있어요" 전체 오류 메시지를 붙여넣으세요 - Claude가 진단하고 수정합니다 Claude는 문맥을 기억하므로 "이제 오류 처리를 추가해줘" 또는 "TypeScript로 변환해줘"라고 요청하세요 --- ## 최신 상태 유지 최신 기능 및 수정 사항을 받으려면 Typecast Skills를 업데이트하세요: ```bash npx skills update ``` --- ## 문제 해결 - 설정에서 스킬이 제대로 설치되었는지 확인 - Claude Code를 다시 시작해보기 - 스킬 저장소에 접근할 수 있는지 확인 - Claude에게 최신 문서를 확인하라고 요청 - 최신 기능을 위해 "ssfm-v30"을 구체적으로 언급 - 로컬에 클론한 경우 스킬 저장소 업데이트 - `YOUR_API_KEY`를 실제 키로 교체했는지 확인 - 유효한 `voice_id`를 사용하고 있는지 확인 - 사용 중인 모델을 API 플랜이 지원하는지 확인 --- ## 리소스 타입캐스트 API 키 받기 사용 가능한 모든 음성 둘러보기 타입캐스트 API 탐색하기 GitHub에서 소스 보기 --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Google Sheets [Google Sheets](https://sheets.google.com/)와 [Apps Script](https://script.google.com/)를 결합하면 스프레드시트에서 바로 텍스트 음성 변환 생성을 자동화할 수 있습니다. 배치 처리, 콘텐츠 자동화, 팀 워크플로우에 완벽합니다! ## 활용 방법 타입캐스트 API와 Google Sheets를 사용하면 다음을 할 수 있습니다: - **배치 TTS 생성** - 한 번의 클릭으로 여러 텍스트를 음성으로 변환 - **워크플로우 자동화** - 데이터를 처리하고 자동으로 오디오 생성 - **코딩 불필요** - 간단한 사용자 정의 메뉴 인터페이스 사용 - **팀 협업** - 팀원들과 시트를 공유하여 협업 오디오 제작 - **Drive에 자동 저장** - 생성된 오디오 파일이 Google Drive에 자동 저장 --- ## 사전 준비 사항 시작하기 전에 다음을 준비하세요: 1. **Google 계정** - Google Sheets 액세스 2. **타입캐스트 API 키** - [여기서 받기](https://studio.typecast.ai/developers/api/) --- ## 설정 가이드 ### 단계 1: 스프레드시트 만들기 1. [Google Sheets](https://sheets.google.com/) 열기 2. 새 빈 스프레드시트 만들기 3. **첫 번째 행**에 헤더로 열 설정: - **A열**: `Text` - 음성으로 변환할 텍스트 - **B열**: `Voice` - Voice ID (예: `tc_66aca22c7d31e45ff05ff418`) 또는 [음성 라이브러리](https://studio.typecast.ai/developers/api/voices)의 음성 이름 - **C열**: `Language` - 언어 코드 (`eng`, `kor`, `jpn`, `cmn` 등) - **D열**: `Audio URL` - 비워두기 (자동으로 채워짐) 4. **두 번째 행**부터 데이터 추가 (1행은 헤더용) Text, Voice, Language, Audio URL 열이 있는 Google Sheet **보이스 이름** (`Arin` 같은) 또는 **voice ID** (`tc_66aca22c7d31e45ff05ff418` 같은)를 사용할 수 있습니다. 스크립트가 이름에서 음성 ID를 자동으로 조회합니다. **[보이스 라이브러리](https://studio.typecast.ai/developers/api/voices)** 또는 [Voices API](https://studio.typecast.ai/developers/api/voices)에서 사용 가능한 보이스와 이름을 찾으세요. 스크립트는 자연스러운 음성을 위해 기본적으로 **스마트 이모션**과 **ssfm-v30 모델**을 사용합니다. ### 단계 2: Apps Script 편집기 열기 1. 메뉴 바에서 **확장 프로그램** 클릭 2. **Apps Script** 선택 Apps Script 옵션을 보여주는 확장 프로그램 메뉴 3. Apps Script 편집기가 있는 새 탭이 열립니다 Apps Script 코드 편집기 인터페이스 ### 단계 3: 타입캐스트 연동 코드 추가 **중요: API 키 필수!** 스크립트를 사용하기 전에 2번째 줄의 `YOUR_API_KEY_HERE`를 실제 타입캐스트 API 키로 반드시 교체해야 합니다. [타입캐스트 API 콘솔](https://studio.typecast.ai/developers/api/)에서 API 키를 받으세요. 1. 기본 `myFunction()` 코드 삭제 2. 다음 코드를 복사하여 붙여넣기: ```javascript // 타입캐스트 API 구성 const TYPECAST_API_KEY = "YOUR_API_KEY_HERE"; // 실제 API 키로 교체 const TYPECAST_API_URL = "https://api.typecast.ai/v1/text-to-speech"; /** * 스프레드시트가 열릴 때 사용자 정의 메뉴 생성 */ function onOpen() { const ui = SpreadsheetApp.getUi(); ui.createMenu("🎙️ Typecast TTS") .addItem("모든 오디오 생성", "generateAllAudio") .addItem("선택한 행만 생성", "generateSelectedAudio") .addSeparator() .addItem("오디오 URL 지우기", "clearAudioUrls") .addToUi(); } /** * 텍스트가 있는 모든 행에 대해 오디오 생성 */ function generateAllAudio() { const sheet = SpreadsheetApp.getActiveSheet(); const lastRow = sheet.getLastRow(); if (lastRow < 2) { SpreadsheetApp.getUi().alert("처리할 데이터가 없습니다!"); return; } // 더 나은 성능을 위해 모든 데이터를 한 번에 가져오기 const dataRange = sheet.getRange(2, 1, lastRow - 1, 4); const data = dataRange.getValues(); let successCount = 0; let errorCount = 0; // 각 행 처리 data.forEach((row, index) => { const text = row[0]; const voiceNameOrId = row[1]; const language = row[2] || "kor"; // 지정되지 않으면 기본값 한국어 const rowNumber = index + 2; // 텍스트 또는 음성 이름/ID가 비어있으면 건너뛰기 if (!text || !voiceNameOrId) { return; } // 오디오 URL이 이미 있으면 건너뛰기 if (row[3]) { return; } try { const audioUrl = callTypecastAPI(text, voiceNameOrId, language); sheet.getRange(rowNumber, 4).setValue(audioUrl); successCount++; // 요청 제한을 피하기 위해 약간의 지연 추가 Utilities.sleep(500); } catch (error) { sheet.getRange(rowNumber, 4).setValue("오류: " + error.message); errorCount++; } }); SpreadsheetApp.getUi().alert( `생성 완료!\n\n성공: ${successCount}\n오류: ${errorCount}`, ); } /** * 선택한 행에 대해서만 오디오 생성 */ function generateSelectedAudio() { const sheet = SpreadsheetApp.getActiveSheet(); const selection = sheet.getActiveRange(); const startRow = selection.getRow(); const numRows = selection.getNumRows(); if (startRow === 1) { SpreadsheetApp.getUi().alert("데이터 행을 선택하세요 (헤더 제외)"); return; } let successCount = 0; let errorCount = 0; for (let i = 0; i < numRows; i++) { const rowNumber = startRow + i; const text = sheet.getRange(rowNumber, 1).getValue(); const voiceNameOrId = sheet.getRange(rowNumber, 2).getValue(); const language = sheet.getRange(rowNumber, 3).getValue() || "kor"; if (!text || !voiceNameOrId) { continue; } try { const audioUrl = callTypecastAPI(text, voiceNameOrId, language); sheet.getRange(rowNumber, 4).setValue(audioUrl); successCount++; Utilities.sleep(500); } catch (error) { sheet.getRange(rowNumber, 4).setValue("오류: " + error.message); errorCount++; } } SpreadsheetApp.getUi().alert( `생성 완료!\n\n성공: ${successCount}\n오류: ${errorCount}`, ); } /** * D열의 모든 오디오 URL 지우기 */ function clearAudioUrls() { const sheet = SpreadsheetApp.getActiveSheet(); const lastRow = sheet.getLastRow(); if (lastRow < 2) { return; } const response = SpreadsheetApp.getUi().alert( "오디오 URL 지우기", "모든 오디오 URL을 지우시겠습니까?", SpreadsheetApp.getUi().ButtonSet.YES_NO, ); if (response === SpreadsheetApp.getUi().Button.YES) { sheet.getRange(2, 4, lastRow - 1, 1).clearContent(); SpreadsheetApp.getUi().alert("오디오 URL이 지워졌습니다!"); } } /** * Voices API를 호출하여 음성 이름에서 음성 ID 가져오기 * @param {string} voiceName - 조회할 음성 이름 * @returns {string} 음성 ID (예: tc_66aca22c7d31e45ff05ff418) */ function getVoiceIdByName(voiceName) { const url = "https://api.typecast.ai/v2/voices"; const options = { method: "get", headers: { "X-API-KEY": TYPECAST_API_KEY, }, muteHttpExceptions: true, }; const response = UrlFetchApp.fetch(url, options); const responseCode = response.getResponseCode(); if (responseCode !== 200) { throw new Error(`음성 가져오기 실패: ${responseCode}`); } const voices = JSON.parse(response.getContentText()); // 이름으로 음성 검색 (대소문자 구분 없음) const searchName = voiceName.toLowerCase().trim(); const voice = voices.find((v) => v.voice_name.toLowerCase() === searchName); if (!voice) { throw new Error( `"${voiceName}" 음성을 찾을 수 없습니다. 음성 이름을 확인하거나 음성 ID를 대신 사용하세요.`, ); } return voice.voice_id; } /** * 타입캐스트 API를 호출하여 음성 생성 * @param {string} text - 음성으로 변환할 텍스트 * @param {string} voiceNameOrId - 음성 이름 (예: "Emily") 또는 음성 ID (예: "tc_66aca22c7d31e45ff05ff418") * @param {string} language - 언어 코드 (eng, kor, jpn, cmn 등) * @returns {string} 생성된 오디오 파일의 URL */ function callTypecastAPI(text, voiceNameOrId, language) { // 입력값 정리 및 검증 text = String(text).trim(); voiceNameOrId = String(voiceNameOrId).trim(); language = String(language || "kor").trim(); if (!text || !voiceNameOrId) { throw new Error("텍스트와 음성 이름/ID가 필요합니다"); } // 입력이 음성 ID인지 이름인지 결정 // 음성 ID는 "tc_"로 시작 let voiceId; if (voiceNameOrId.startsWith("tc_")) { voiceId = voiceNameOrId; Logger.log("음성 ID 사용: " + voiceId); } else { // 음성 이름이므로 ID 조회 Logger.log("음성 이름 조회 중: " + voiceNameOrId); voiceId = getVoiceIdByName(voiceNameOrId); Logger.log("음성 ID 찾음: " + voiceId); } // 디버깅용 로그 Logger.log("API 호출 중, 텍스트: " + text); Logger.log("언어: " + language); const payload = { voice_id: voiceId, text: text, model: "ssfm-v30", language: language, prompt: { emotion_type: "smart", // 자연스러운 음성을 위한 스마트 이모션 활성화 }, output: { audio_format: "mp3", }, }; // 디버깅용 페이로드 로그 Logger.log("페이로드: " + JSON.stringify(payload)); const options = { method: "post", contentType: "application/json", headers: { "X-API-KEY": TYPECAST_API_KEY, }, payload: JSON.stringify(payload), muteHttpExceptions: true, }; const response = UrlFetchApp.fetch(TYPECAST_API_URL, options); const responseCode = response.getResponseCode(); if (responseCode !== 200) { const errorBody = response.getContentText(); Logger.log("오류 응답: " + errorBody); throw new Error(`API 오류: ${responseCode} - ${errorBody}`); } // 오디오 blob 가져오기 const audioBlob = response.getBlob(); // Google Drive에 업로드 const folder = DriveApp.getRootFolder(); // 또는 특정 폴더 지정 const fileName = `typecast_${new Date().getTime()}.mp3`; const file = folder.createFile(audioBlob.setName(fileName)); // 링크로 접근 가능하도록 파일 설정 file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW); // 파일 URL 반환 return file.getUrl(); } ``` 편집기의 완전한 Apps Script 코드 **🔑 API 키를 잊지 마세요!** 스크립트를 실행하기 전에 2번째 줄을 반드시 업데이트해야 합니다: **이것을:** ```javascript const TYPECAST_API_KEY = 'YOUR_API_KEY_HERE'; ``` **실제 API 키로:** ```javascript const TYPECAST_API_KEY = 'your_actual_api_key_from_typecast'; ``` [타입캐스트 API 콘솔](https://studio.typecast.ai/developers/api/)에서 API 키를 받으세요. 3. **저장** 아이콘 (💾)을 클릭하거나 `⌘+S` (Mac) / `Ctrl+S` (Windows)를 누르세요 4. 프로젝트에 이름을 지정하세요 (예: "타입캐스트 TTS 통합") ### 단계 4: 권한 승인 스크립트를 처음 실행할 때 Google이 권한을 요청합니다: 1. Apps Script 탭을 닫고 스프레드시트로 돌아가기 2. 페이지 새로고침 (F5 또는 `⌘+R`) 3. 메뉴 바에 새 메뉴 **🎙️ 타입캐스트 TTS**가 나타나야 합니다 모든 오디오 생성, 선택한 행만 생성, 오디오 URL 지우기 옵션을 보여주는 사용자 정의 메뉴 4. **🎙️ 타입캐스트 TTS** → **모든 오디오 생성** 클릭 모든 사용 가능한 옵션을 보여주는 열린 타입캐스트 TTS 메뉴 5. Google이 스크립트 승인을 요청합니다: - **계속** 클릭 - Google 계정 선택 - **고급** → **프로젝트 이름(으)로 이동 (안전하지 않음)** 클릭 - **허용** 클릭 "안전하지 않음" 경고는 사용자 정의 스크립트이기 때문에 나타납니다. 복사한 코드를 신뢰한다면 진행해도 안전합니다. --- ## 사용법 ### 모든 행에 대해 음성 생성 1. **🎙️ 타입캐스트 TTS** → **모든 오디오 생성** 클릭 2. 스크립트가 텍스트와 음성 ID가 있는 모든 행을 처리합니다 생성 진행 중을 보여주는 실행 중인 스크립트 알림 3. 완료되면 성공 메시지가 표시됩니다 3개의 오디오 파일이 생성되고 0개의 오류를 보여주는 성공 대화상자 4. D열에 오디오 URL이 나타납니다 5. 생성된 오디오 파일이 Google Drive에 저장됩니다 **스마트 이모션이 기본적으로 활성화되어 있습니다!** 스크립트는 자연스럽고 감정적으로 적절한 음성을 위해 `ssfm-v30` 모델과 함께 `emotion_type: 'smart'`를 사용합니다. 스마트 이모션이 활성화된 상태로 D열에 생성된 Google Drive URL을 보여주는 스프레드시트 ### 선택한 행에 대해 음성 생성 1. 처리할 행 선택 (행 번호를 클릭하고 드래그) 2. **🎙️ 타입캐스트 TTS** → **선택한 행만 생성** 클릭 3. 선택한 행만 처리됩니다 특정 오디오 파일을 재생성하거나 점진적으로 새 행을 추가할 때 "선택한 행만 생성"을 사용하세요. ### 음성 URL 지우기 모든 생성된 URL을 제거하려면 (Drive에서 오디오 파일은 삭제되지 않음): 1. **🎙️ 타입캐스트 TTS** → **오디오 URL 지우기** 클릭 2. 작업 확인 3. D열의 모든 URL이 지워집니다 --- ## 고급 설정 ### 스마트 이모션 (기본값) 스크립트는 기본적으로 **스마트 이모션**을 사용하며, 텍스트를 자동으로 분석하여 적절한 감정을 적용합니다: ```javascript prompt: { emotion_type: "smart"; // 텍스트에 가장 적합한 감정을 자동으로 감지 } ``` ### 감정 프리셋 사용 감정을 수동으로 제어하려면 `emotion_type`을 `preset`으로 변경하세요: ```javascript function callTypecastAPI(text, voiceId, language) { const payload = { voice_id: voiceId, text: text, model: "ssfm-v30", language: language, prompt: { emotion_type: "preset", emotion_preset: "happy", // 옵션: happy, sad, angry, whisper, normal, toneup, 또는 tonedown. emotion_intensity: 1.0, // 0.0 ~ 1.0 }, output: { audio_format: "mp3", audio_tempo: 1.0, // 속도: 0.5 (느림) ~ 2.0 (빠름) audio_pitch: 0, // 피치: -12 ~ +12 반음 volume: 100, // 볼륨: 0 ~ 200 }, }; // ... 나머지 코드 } ``` ### 지원 언어 스크립트는 ssfm-v30 모델(기본 사용)로 **37개 언어**를 지원합니다: | 코드 | 언어 | 코드 | 언어 | 코드 | 언어 | | ----- | --------- | ----- | ---------- | ----- | ---------- | | `ara` | 아랍어 | `ind` | 인도네시아어 | `por` | 포르투갈어 | | `ben` | 벵골어 | `ita` | 이탈리아어 | `ron` | 루마니아어 | | `bul` | 불가리아어 | `jpn` | 일본어 | `rus` | 러시아어 | | `ces` | 체코어 | `kor` | 한국어 | `slk` | 슬로바키아어 | | `dan` | 덴마크어 | `msa` | 말레이어 | `spa` | 스페인어 | | `deu` | 독일어 | `nan` | 민난어 | `swe` | 스웨덴어 | | `ell` | 그리스어 | `nld` | 네덜란드어 | `tam` | 타밀어 | | `eng` | 영어 | `nor` | 노르웨이어 | `tgl` | 타갈로그어 | | `fin` | 핀란드어 | `pan` | 펀자브어 | `tha` | 태국어 | | `fra` | 프랑스어 | `pol` | 폴란드어 | `tur` | 터키어 | | `hin` | 힌디어 | `ukr` | 우크라이나어 | `vie` | 베트남어 | | `hrv` | 크로아티아어 | `yue` | 광둥어 | `zho` | 중국어 | | `hun` | 헝가리어 | | | | | 다른 언어를 사용하려면 C열의 값을 변경하면 됩니다! 언어 코드는 대소문자를 구분하지 않습니다 (`ENG`와 `eng` 모두 작동). ### 특정 Drive 폴더에 저장 오디오 파일을 루트 대신 특정 폴더에 저장하려면: ```javascript // 이 줄을: const folder = DriveApp.getRootFolder(); // 이것으로 교체 (폴더 ID 사용): const folder = DriveApp.getFolderById("YOUR_FOLDER_ID_HERE"); // 또는 새 폴더 생성: const folder = DriveApp.createFolder("Typecast Audio Files"); ``` ### 추가 열 설정 더 세밀한 제어를 위해 추가 열로 스프레드시트를 확장할 수 있습니다: - **E열**: 감정 프리셋 (happy, sad, angry, normal) - **F열**: 오디오 템포 (0.5 ~ 2.0) - **G열**: 오디오 피치 (-12 ~ +12) - **H열**: 상태 (Processing, Done, Error) 그런 다음 스크립트를 수정하여 시트에서 이 값들을 읽도록 합니다. --- ## Google Sheets와 타입캐스트 API를 사용해야 하는 이유 간단한 복사-붙여넣기 설정. 개발자가 아니어도 단 5분 만에 전문적인 보이스오버를 쉽게 생성할 수 있습니다. 한 번 설정하면 영구적으로 사용 가능. 반복적인 TTS 작업과 매크로 스타일 워크플로우에 이상적입니다. 한 번의 클릭으로 수백 개의 오디오 파일을 생성합니다. 전체 콘텐츠 캘린더를 한 번에 처리할 수 있습니다. 팀과 스프레드시트를 공유하세요. 모두가 텍스트를 관리하고 함께 오디오를 생성할 수 있습니다. **타입캐스트 API는 간단합니다.** 몇 줄의 코드와 Google Sheets만으로 전체 TTS 워크플로우를 자동화할 수 있습니다. 기술적 전문 지식 없이 대규모로 오디오를 생성해야 하는 콘텐츠 크리에이터, 마케터, 교육자에게 적합합니다. --- ## 활용 사례 수업 스크립트에서 강의 내레이션을 만드세요. A열에 텍스트를 추가하고 오디오를 생성한 다음 동영상에 사용할 수 있습니다. 공유 Google Sheet에서 인트로/아웃트로 세그먼트, 광고 읽기, 안내 멘트를 생성하세요. 상품 설명을 접근성 향상이나 마케팅 동영상을 위한 오디오로 변환하세요. 콘텐츠 캘린더에서 Instagram Reels, TikTok, YouTube Shorts용 보이스오버를 일괄 생성하세요. 시트에서 텍스트를 번역하고 글로벌 사용자를 위해 여러 언어로 오디오를 생성하세요. --- ## 문제 해결 - Apps Script 편집기에서 스크립트를 저장했는지 확인 - `onOpen` 함수를 수동으로 실행해 보기: 1. Apps Script 편집기로 돌아가기 2. 함수 드롭다운에서 `onOpen` 선택 3. **실행** 버튼 (▶️) 클릭 - 브라우저 콘솔에서 오류 확인 (F12) - 프롬프트가 나타났을 때 **허용**을 클릭했는지 확인 - 권한을 지우고 다시 권한 승인 시도: 1. Apps Script 편집기 → 실행 → 권한 지우기 2. 저장하고 닫기 3. 스프레드시트 새로고침 4. 메뉴 다시 시도 - `YOUR_API_KEY_HERE`를 실제 API 키로 교체했는지 확인 - [타입캐스트 API 콘솔](https://studio.typecast.ai/developers/api/)에서 API 키 확인 - 키 주변에 여분의 공백이 없는지 확인 - [타입캐스트 API 콘솔](https://studio.typecast.ai/developers/api/usage)에서 크레딧 잔액 확인 - 크레딧은 글자 수에 기반하여 차감 - 충분한지 확인 - "선택한 행만 생성"을 사용하여 더 작은 배치로 처리 - Apps Script는 6분 실행 제한이 있음 - 매우 큰 데이터셋(500+ 행)은 여러 시트로 분할 - Google Drive 루트 폴더 확인 - 오디오 파일 이름은 `typecast_[timestamp].mp3`입니다 - 스크립트에 Drive 권한이 있는지 확인 (올바르게 승인됨) --- ## 참고 자료 600+개 사용 가능한 음성 둘러보기 타입캐스트 API 탐색하기 Google Apps Script에 대해 더 알아보기 타입캐스트 API 키 받기 --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Zapier [Zapier](https://zapier.com/)는 가장 인기 있는 워크플로우 자동화 플랫폼입니다. 타입캐스트 통합을 사용하면 코딩 없이도 텍스트를 음성으로 자동 변환할 수 있습니다! ## 할 수 있는 것 타입캐스트 Zapier 통합을 사용하면 다음을 할 수 있습니다: - 모든 텍스트에서 **내레이션 자동 생성** - 다양한 성별, 나이, 스타일의 **600+개 음성 중 선택** - **감정 적용** (happy, sad, angry, whisper 등) - 문맥 인식 음성 합성을 위한 **스마트 이모션 사용** (ssfm-v30) - 텍스트 설명으로 **음성 추천**을 받고, 필요하면 합성 전에 음성 상세 정보를 조회 - **다른 앱과 연결** - 이메일로 오디오 전송, 클라우드 스토리지에 저장, Slack에 게시 등 --- ## 사전 요구 사항 시작하기 전에 다음을 준비하세요: 1. **Zapier 계정** - 없다면 [여기서 가입](https://zapier.com/sign-up)하세요 2. **타입캐스트 API 키** - [여기서 받기](https://studio.typecast.ai/developers/api/) --- ## 설치 ### 단계 1: 타입캐스트 계정 연결 Zap에서 타입캐스트를 처음 사용할 때: 1. 타입캐스트 계정 연결 메시지가 표시됩니다 2. **타입캐스트 API 키**를 입력하세요 3. **Yes, Continue**를 클릭하세요 [타입캐스트 API 콘솔](https://studio.typecast.ai/developers/api)에서 API 키를 받을 수 있습니다. --- ## 빠른 시작: 첫 번째 음성 생성 매시간 음성을 생성하는 간단한 Zap을 만들어 봅시다! ### 단계 1: 새 Zap 만들기 1. [Zapier 대시보드](https://zapier.com/app/assets/zaps)로 이동하세요 2. **\+ Create** → **New Zap**을 클릭하세요 ![Create 버튼이 있는 Zapier Zaps 대시보드](/images/zapier-zaps-dashboard.webp) ### 단계 2: 트리거 설정 이 예제에서는 Schedule 트리거를 사용합니다: 1. **Trigger** 단계를 클릭하세요 2. **Schedule**을 검색하고 선택하세요 3. 이벤트로 **Every Hour**를 선택하세요 4. **Continue**를 클릭하고 **Test trigger**를 클릭하세요 ![Image](/images/image-5.webp) ### 단계 3: **타입캐스트** 액션 추가 1. **Action** 단계를 클릭하세요 2. **Typecast**를 검색하세요 3. **Typecast**를 선택하세요 ![Image](/images/image-2.webp) 4. 이벤트로 **Create Speech From Text**를 선택하세요 ![Image](/images/image-3.webp) ### 단계 4: 텍스트 음성 변환 구성 ![Image](/images/image-6.webp) | 설정 | 입력할 내용 | | --- | --- | | **Text** | 변환할 텍스트 (필수) | | **Model** | `ssfm-v30 (권장)` - 최고 품질의 최신 모델 | | **Voice** | 드롭다운에서 선택 (필수) | | **Language** | 자동 감지 또는 수동 선택 | | **Emotion Type** | `Preset` 또는 `Smart` (v30만 해당) | | **Emotion Preset** | Normal, Happy, Sad, Angry, Whisper 등 | ### 단계 5: 테스트 및 게시 1. **Continue**를 클릭하여 Test 단계로 이동하세요 2. **Test step**을 클릭하여 샘플 오디오를 생성하세요 3. 성공하면 **Publish**를 클릭하여 Zap을 활성화하세요 생성된 오디오 URL은 출력 데이터로 사용 가능합니다. 이메일로 전송, 클라우드 스토리지에 업로드, Slack에 게시하는 등 후속 단계에서 사용할 수 있습니다. --- ## 사용 가능한 액션 ### Create Speech From Text 타입캐스트 AI 음성 모델을 사용하여 텍스트를 음성으로 변환합니다. **입력:** | 필드 | 필수 | 설명 | | --- | --- | --- | | Text | 예 | 변환할 텍스트 (최대 2000자) | | Model | 예 | `ssfm-v30` (권장) 또는 `ssfm-v21` | | Voice | 예 | 600+개 음성 중 선택 | | Language | 아니오 | ISO 639-3 코드 (미설정 시 자동 감지) | | Emotion Type | 아니오 | `Preset` 또는 `Smart` (문맥 인식, v30만 해당) | | Emotion Preset | 아니오 | Normal, Happy, Sad, Angry, Whisper, Tone Up, Tone Down | | Emotion Intensity | 아니오 | 0.0 ~ 2.0 (기본값: 1.0) | | Volume | 아니오 | 0 ~ 200 (기본값: 100) | | Audio Pitch | 아니오 | -12 ~ \+12 반음 (기본값: 0) | | Audio Tempo | 아니오 | 0.5x ~ 2.0x 속도 (기본값: 1.0) | | Audio Format | 아니오 | WAV 또는 MP3 | | Seed | 아니오 | 재현 가능한 결과를 위해 | **출력:** - 오디오 파일 URL - Speech ID - 길이 (초) - Content Type ### List Voices (검색) 향상된 메타데이터와 함께 사용 가능한 모든 음성 모델을 나열합니다. **필터:** - Model (ssfm-v30, ssfm-v21) - Gender (Male, Female) - Age (Child, Teenager, Young Adult, Middle Age, Elder) - Use Cases (Audiobook, Podcast, E-learning 등) ### Get Voice by ID (검색) 모델별 지원 감정을 포함한 특정 음성에 대한 자세한 정보를 가져옵니다. ### Recommend Voices (검색) 텍스트 설명으로 후보 음성을 찾습니다. **입력:** | 필드 | 필수 | 설명 | | --- | --- | --- | | Query | 예 | 원하는 스타일, 분위기, 언어, 사용 사례, 콘텐츠 맥락을 설명하는 텍스트 | | Count | 아니오 | 반환할 추천 개수 (1~10, 기본값: 5) | **출력:** - Voice ID - Voice Name - Score 추천 응답에는 `voice_id`, `voice_name`, `score`만 포함되므로, Zap에서 합성 전에 상세 메타데이터가 필요하면 List Voices 또는 Get Voice by ID를 사용하세요. --- ## 감정 설정 감정 제어로 음성을 표현력 있게 만드세요! ### SSFM-V30(최신 모델)용 감정을 추가하는 두 가지 방법: AI가 텍스트 맥락에서 최적의 감정을 자동으로 감지합니다. 자연스러운 대화와 스토리텔링에 적합합니다. 더 나은 문맥 이해를 위해 "Previous Text"와 "Next Text"를 추가하세요. 7가지 감정 중에서 수동으로 선택합니다: Normal, Happy, Sad, Angry, Whisper, Tone Up, Tone Down. ### SSFM-V21용 4가지 감정 중에서 선택: `Normal`, `Happy`, `Sad`, `Angry` **Emotion Intensity** (0.0 - 2.0) 조절: - `0.0` - 완전히 중립 - `1.0` - 표준 (기본값) - `2.0` - 최대 강도 --- ## 활용 사례 예시 1. **트리거**: 새 기사가 있는 RSS 피드 2. **액션**: 타입캐스트가 기사 요약으로 오디오 생성 3. **액션**: 팟캐스트 호스팅 플랫폼에 업로드 1. **트리거**: 새 지원 티켓 2. **액션**: AI가 응답 텍스트 생성 3. **액션**: 타입캐스트가 음성 메시지로 변환 4. **액션**: 이메일 또는 SMS로 전송 1. **트리거**: Google Sheets의 새 레슨 콘텐츠 2. **액션**: 타입캐스트가 내레이션 생성 3. **액션**: Google Drive에 업로드 4. **액션**: Slack으로 팀에 알림 --- ## 문제 해결 Zapier 앱 디렉토리에서 "Typecast"를 검색하세요. 사용 가능한 최신 버전을 사용하고 있는지 확인하세요. - API 키가 올바른지 확인하세요 - [타입캐스트 API 콘솔](https://studio.typecast.ai/developers/api/)에서 키를 확인하세요 - 키에 여분의 공백이 없는지 확인하세요 - API 키에 적절한 권한이 있는지 확인하세요 - 새로고침 아이콘을 클릭하여 필드를 새로고침해 보세요 - 텍스트가 비어 있지 않은지 확인하세요 - API 크레딧이 충분한지 확인하세요 - 테스트 출력에서 오류 메시지를 확인하세요 --- ## 리소스 사용 가능한 모든 음성 둘러보기 타입캐스트 API 탐색하기 Zapier 문서 및 지원 ## 무음 길이 조절 Typecast **2.2.7 이상**의 일반·스트리밍·타임스탬프 음성 생성 작업에서 **Remaining Silence (ms)**를 설정하세요. 입력 키는 `remove_silence_ms`이며 빈칸은 비활성화입니다. `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. 일반·스트리밍·타임스탬프 TTS는 `output.remove_silence_ms`, Compose는 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 전달합니다. 반환 타임스탬프는 처리 후 오디오를 기준으로 하며, 명시적인 `pause` 세그먼트는 유지됩니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Make [Make](https://www.make.com/)는 강력한 시각적 워크플로우 자동화 플랫폼입니다. Typecast 통합을 사용하면 코딩 없이 텍스트를 음성으로 자동 변환할 수 있습니다! ## 할 수 있는 것 Typecast Make 통합을 사용하면 다음을 할 수 있습니다: - 모든 텍스트에서 **내레이션 자동 생성** - 다양한 성별, 나이, 스타일의 **600+개 음성 중 선택** - **감정 적용** (happy, sad, angry, whisper 등) - 문맥 인식 음성 합성을 위한 **스마트 이모션 사용** (ssfm-v30) - 성별, 연령대, 사용 사례별 **음성 필터링** - **1500개 이상의 앱과 연결** - 이메일로 오디오 전송, 클라우드 스토리지에 저장, Slack에 게시 등 --- ## 사전 요구 사항 시작하기 전에 다음을 준비하세요: 1. **Make 계정** - 없다면 [여기서 가입](https://www.make.com/en/register)하세요 2. **타입캐스트 API 키** - [여기서 받기](https://studio.typecast.ai/developers/api/) 3. **초대 링크 접근** - [초대 수락](https://www.make.com/en/hq/app-invitation/19c62bf73b6afd22b41a2318e3f0a57e) --- ## 설치 ### 단계 1: 초대 수락 Typecast는 현재 비공개 베타 중이므로 먼저 초대를 수락해야 합니다: 1. [초대 링크](https://www.make.com/en/hq/app-invitation/19c62bf73b6afd22b41a2318e3f0a57e)를 클릭하세요 2. Make 계정에 로그인하세요 (또는 새로 만드세요) 3. **Install**을 클릭하여 앱에 Typecast를 추가하세요 ### 단계 2: Typecast 계정 연결 시나리오에서 Typecast를 처음 사용할 때: 1. **Create a connection** 클릭 2. 연결 이름 입력 (예: "My Typecast") 3. **타입캐스트 API 키** 입력 4. **Save** 클릭 [타입캐스트 API 콘솔](https://studio.typecast.ai/developers/api/)에서 API 키를 받을 수 있습니다. --- ## 빠른 시작: 첫 번째 음성 생성 음성을 생성하는 간단한 시나리오를 만들어 봅시다! ### 단계 1: 새 시나리오 만들기 1. [Make 대시보드](https://www.make.com/)로 이동하세요 2. **\+ Create a new scenario** 클릭 ### 단계 2: Typecast 모듈 추가 1. **\+** 버튼을 클릭하여 모듈 추가 2. **Typecast** 검색 3. 결과에서 **Typecast** 선택 ![Make에서 Typecast 검색](/images/make-search-typecast.png) ### 단계 3: 액션 선택 사용 가능한 액션에서 **Generate a Speech**를 선택하세요. ![Typecast 액션 옵션 - Generate a Speech, Get Voices](/images/make-typecast-actions.png) ### 단계 4: 텍스트 음성 변환 구성 ![Make의 Typecast 구성 필드](/images/make-typecast-configure.png) | 설정 | 입력할 내용 | | --- | --- | | **Text** | 변환할 텍스트 (필수) | | **Voice ID** | 음성 ID 입력 (예: `tc_60e5426de8b95f1d3000d7b5`) | | **Model** | `ssfm-v30` - 최고 품질의 최신 모델 | | **Language** | 자동 감지 또는 수동 선택 | | **Emotion Type** | `Preset` 또는 `Smart` (ssfm-v30만 해당) | 먼저 **Get Voices** 모듈을 사용하여 사용 가능한 Voice ID를 찾으세요. 성별, 나이, 사용 사례로 필터링할 수 있습니다! ### 단계 5: 테스트 및 활성화 1. **OK**를 클릭하여 모듈 구성 저장 2. **Run once**를 클릭하여 시나리오 테스트 3. 성공하면 시나리오를 **ON**으로 전환하여 활성화 생성된 오디오는 바이너리 데이터(WAV 또는 MP3)로 반환됩니다. 이메일로 전송, 클라우드 스토리지에 업로드, 추가 처리 등 후속 모듈에서 사용할 수 있습니다. --- ## 사용 가능한 모듈 ### Generate a Speech (액션) Typecast AI 음성 모델을 사용하여 텍스트를 음성으로 변환합니다. **입력:** | 필드 | 필수 | 설명 | | --- | --- | --- | | Text | 예 | 변환할 텍스트 (최대 2000자) | | Voice ID | 예 | 음성 식별자 (형식: `tc_xxxxx`) | | Model | 예 | `ssfm-v30` (권장) 또는 `ssfm-v21` | | Language | 아니오 | ISO 639-3 코드 (미설정 시 자동 감지) | | Emotion Type | 아니오 | `Preset` 또는 `Smart` (문맥 인식, ssfm-v30만 해당) | | Emotion Preset | 아니오 | Normal, Happy, Sad, Angry, Whisper, Tone Up, Tone Down | | Emotion Intensity | 아니오 | 0.0 ~ 2.0 (기본값: 1.0) | | Target LUFS | 아니오 | -70 ~ 0 (예: 스트리밍 -14, 방송 -23) | | Pitch | 아니오 | -12 ~ \+12 반음 (기본값: 0) | | Tempo | 아니오 | 0.5x ~ 2.0x 속도 (기본값: 1.0) | | Audio Format | 아니오 | WAV 또는 MP3 | | Seed | 아니오 | 재현 가능한 결과를 위해 | **출력:** - 오디오 파일 (바이너리 데이터) - 파일 이름 ### Get Voices (검색) 필터링 옵션과 함께 사용 가능한 모든 음성 모델을 나열합니다. **필터:** | 필드 | 설명 | | --- | --- | | Model | `ssfm-v30` 또는 `ssfm-v21`로 필터링 | | Gender | Male 또는 Female | | Age | Child, Teenager, Young Adult, Middle Age, Elder | | Use Cases | Audiobook, Podcast, E-learning, Ads, Game 등 | | Limit | 최대 결과 수 | **출력 (음성별):** - Voice ID - Voice Name - 지원 모델 및 감정 - 성별 및 연령대 - 권장 사용 사례 --- ## 올바른 음성 찾기 프로젝트에 완벽한 음성을 찾으려면: 시나리오에 **Get Voices** 모듈을 추가하세요 성별, 연령대, 사용 사례로 필터링하여 옵션 좁히기 결과에서 사용할 `voice_id`를 복사하세요 **Generate a Speech** 모듈에 Voice ID를 붙여넣으세요 --- ## 감정 설정 감정 제어로 음성을 표현력 있게 만드세요! ### ssfm-v30 (최신 모델)용 감정을 추가하는 두 가지 방법: AI가 텍스트 맥락에서 최적의 감정을 자동으로 감지합니다. 자연스러운 대화와 스토리텔링에 적합합니다. 더 나은 문맥 이해를 위해 "Previous Text"와 "Next Text"를 추가하세요. 7가지 감정 중에서 수동으로 선택합니다: Normal, Happy, Sad, Angry, Whisper, Tone Up, Tone Down. **Emotion Type**을 선택하면 추가 필드가 동적으로 나타납니다: - **Preset**: "Emotion Preset" 드롭다운과 "Emotion Intensity" 슬라이더 표시 - **Smart**: "Previous Text"와 "Next Text" 문맥 필드 표시 ![Make의 Preset 감정 설정](/images/make-emotion-preset.png) ![Make의 문맥이 있는 Smart 감정 설정](/images/make-emotion-smart.png) ### ssfm-v21용 4가지 감정 중에서 선택: `Normal`, `Happy`, `Sad`, `Angry` **Emotion Intensity** (0.0 - 2.0) 조절: - `0.0` - 완전히 중립 - `1.0` - 표준 (기본값) - `2.0` - 최대 강도 --- ## 모델 비교 | 기능 | ssfm-v21 | ssfm-v30 | | --- | --- | --- | | 감정 | 4가지 | 7가지 | | 언어 | 27개 | 37개 | | 스마트 모드 | 아니오 | 예 | | 품질 | 안정적 | 향상됨 | | 권장 | 프로덕션 | 최신 기능 | --- ## 활용 사례 예시 1. **트리거**: Google Sheets에 새 행 추가 2. **액션**: Typecast가 스크립트 텍스트로 내레이션 생성 3. **액션**: Google Drive에 오디오 업로드 4. **액션**: Slack으로 팀에 알림 1. **트리거**: CMS의 새 콘텐츠 2. **액션**: DeepL 또는 Google Translate로 텍스트 번역 3. **액션**: Typecast가 각 언어로 오디오 생성 4. **액션**: 언어별로 정리된 클라우드 스토리지에 저장 1. **트리거**: 새 레슨 모듈 승인 2. **액션**: Typecast가 전문 내레이션 생성 3. **액션**: LMS(Teachable, Thinkific 등)에 업로드 4. **액션**: Airtable에서 코스 상태 업데이트 1. **트리거**: 매일 오전 6시 예약 2. **액션**: 최신 뉴스 헤드라인 가져오기 3. **액션**: Typecast가 에너지 넘치는 음성으로 인트로 생성 4. **액션**: 팟캐스트 오디오 파일에 추가 --- ## 문제 해결 먼저 [초대 링크](https://www.make.com/en/hq/app-invitation/19c62bf73b6afd22b41a2318e3f0a57e)를 수락했는지 확인하세요. Typecast는 현재 비공개 베타 중입니다. - API 키가 올바른지 확인하세요 - [타입캐스트 API 콘솔](https://studio.typecast.ai/developers/api/)에서 키를 확인하세요 - 키에 여분의 공백이 없는지 확인하세요 - Voice ID는 `tc_` 접두사로 시작해야 합니다 - **Get Voices** 모듈을 사용하여 유효한 Voice ID를 찾으세요 - 음성이 선택한 모델을 지원하는지 확인하세요 - 텍스트가 비어 있지 않은지 확인하세요 - API 크레딧이 충분한지 확인하세요 - 텍스트가 2000자를 초과하는지 확인하세요 - 스마트 이모션은 `ssfm-v30` 모델에서만 사용 가능합니다 - Emotion Type으로 "Smart"를 선택했는지 확인하세요 - Previous Text 및/또는 Next Text 필드에 문맥을 제공하세요 --- ## 리소스 Typecast 사용을 위해 초대 수락 사용 가능한 모든 음성 둘러보기 타입캐스트 API 탐색하기 Make 문서 및 지원 --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # n8n [n8n](https://n8n.io/)은 시각적 워크플로우 자동화 도구입니다. 타입캐스트 노드를 사용하면 코딩 없이 텍스트를 음성으로 자동 변환할 수 있습니다! ## 활용 방안 타입캐스트 n8n 노드를 사용하면 다음을 할 수 있습니다: - **콘텐츠 제작 자동화** - RSS 피드, AI 작성, 타입캐스트 음성을 결합하여 완전 자동화된 YouTube 또는 TikTok 채널 구축 - **고객 경험 향상** - 고객이 구매할 때 WhatsApp 또는 이메일로 개인화된 음성 메시지 전송 - **도달 범위 확대** - 팟캐스트 또는 비디오 콘텐츠를 여러 언어로 자동 번역 및 더빙 - **음성 추천** - 자연어 설명으로 후보 음성을 검색 - **알림 받기** - 중요한 시스템 업데이트 또는 판매 마일스톤에 대해 Slack 또는 Discord에서 맞춤 음성 알림 --- ## 사전 준비 사항 시작하기 전에 다음을 준비하세요: 1. **n8n** 설치 ([n8n Cloud](https://n8n.io/) 또는 자체 호스팅) 2. **타입캐스트 API 키** - [여기서 받기](https://studio.typecast.ai/developers/api/) --- ## 설치 ### 단계 1: 타입캐스트 노드 설치 n8n Cloud를 사용하는 경우 별도의 설치가 필요 없습니다. **빠른 시작** 섹션으로 바로 건너뛰세요. 노드를 검색할 때 설치 버튼이 나타나면 **Install node**를 클릭하여 설정을 완료하세요. 1. n8n 설치 디렉토리에서 다음 명령을 실행하세요: ```bash npm install @neosapience/n8n-nodes-typecast ``` 2. n8n을 다시 시작하세요. --- ## 빠른 시작: 첫 번째 음성 생성 첫 번째 텍스트 음성 변환 워크플로우를 만들어 보세요. ### 단계 1: 타입캐스트 노드 추가 1. 새 워크플로우 만들기 2. **+** 버튼을 클릭하여 노드 추가 3. **Typecast** 검색 4. **Typecast** 선택 n8n에서 Typecast 노드 검색 5. 목록에서 액션 선택 (예: **Convert text to speech**) Typecast 노드 액션 목록 ### 단계 2: API 키 연결 (인증 정보) 노드를 선택한 후 타입캐스트 API와 연결하기 위해 API 키를 구성해야 합니다. 1. 노드 설정 패널에서 **Credential to connect with** 필드를 클릭하세요. 2. **- Create New Credential -** 항목을 선택하세요. 3. [타입캐스트 API 콘솔](https://studio.typecast.ai/developers/api/)에서 복사할 수 있는 API 키를 입력하세요. 4. **Create**를 클릭하여 자격 증명을 저장하세요. n8n에서 새 자격 증명 만들기 ### 단계 3: 텍스트 음성 변환 구성 | 설정 | 입력할 내용 | |---------|---------------| | **Resource** | `Speech` | | **Operation** | `Text to Speech` | | **Voice ID** | 드롭다운에서 음성 선택 (이름, 성별, 나이, 감정 표시) | | **Text** | 변환할 텍스트 | | **Model** | `ssfm-v30` - 최고 품질 권장 | #### 보이스 선택 Voice ID 필드에서 보이스를 쉽게 찾을 수 있습니다: 1. Voice ID 드롭다운을 클릭하세요 2. 세부 정보(이름, 성별, 나이, 사용 가능한 감정)와 함께 보이스 둘러보기 3. 이름이나 특성으로 필터링하는 검색 사용 4. 원하는 보이스 선택 음성 세부 정보가 있는 Voice ID 드롭다운 Voice ID를 직접 입력하려면 "By ID" 모드로 전환할 수도 있습니다 (예: `tc_60e5426de8b95f1d3000d7b5`). 구성된 Typecast Text to Speech #### 감정 설정 감정 조절로 음성을 표현력 있게 만드세요! **ssfm-v30용** 감정을 추가하는 두 가지 방법: AI가 텍스트 맥락에서 최적의 감정을 자동으로 감지합니다. 자연스러운 대화와 스토리텔링에 적합합니다. 7가지 감정 중에서 수동으로 선택합니다: Normal, Happy, Sad, Angry, Whisper, Tone Up, Tone Down. **스마트 이모션 팁:** AI가 맥락을 더 잘 이해하도록 "Previous Text"와 "Next Text"를 추가하세요! **ssfm-v21용** 4가지 감정 중에서 선택: `Normal`, `Happy`, `Sad`, `Angry` Normal, Happy, Sad, Angry 옵션을 보여주는 감정 프리셋 드롭다운 **Emotion Intensity** (0.0 - 2.0) 조절: - `0.0` - 낮은 강도 - `1.0` - 표준 (기본값) - `2.0` - 최대 강도 ssfm-v21 모델용 감정 설정 #### 추가 옵션 오디오 출력 사용자 정의: | 옵션 | 설명 | 기본값 | |--------|-------------|---------| | **Audio Format** | `WAV` (고품질) 또는 `MP3` (작은 크기) | WAV | | **Audio Pitch** | 피치 조절 (-12 ~ +12 반음) | 0 | | **Audio Tempo** | 속도 조절 (0.5x ~ 2.0x) | 1.0 | | **Language** | 필요한 경우 자동 감지 재정의 | 자동 감지 | | **Seed** | 부호 없는 정수 (≥ 0). 재현 가능한 출력을 위해 동일한 시드 사용 | 랜덤 | ### 단계 4: 실행 및 듣기 1. 노드 연결 (Manual Trigger → Typecast) 2. **Execute Workflow** 클릭 3. 출력 확인 - 오디오 파일이 준비되었습니다! 4. 오디오를 클릭하여 재생 연결된 Typecast 노드가 있는 n8n 워크플로우 생성된 오디오는 `data`라는 이름의 바이너리 파일로 나타납니다. 저장, 이메일 전송, 어디든 보낼 수 있습니다! --- ## 완벽한 음성 찾기 ### 모든 보이스 둘러보기 1. Typecast 노드 추가 2. **Resource** → `Voice` 설정 3. **Operation** → `Get All Voices` 설정 4. 노드를 실행하여 사용 가능한 모든 음성 확인 ### 음성 추천 1. Typecast 노드 추가 2. **Resource** → `Voice` 설정 3. **Operation** → `Recommend Voices` 설정 4. 텍스트 설명을 입력하고 노드 실행 추천 결과에는 `voice_id`, `voice_name`, `score`만 포함됩니다. 합성 전에 지원 모델, 감정, 성별, 나이대, 사용 사례 같은 메타데이터가 필요하면 **Get Voice** 또는 **Get All Voices**를 사용하세요. ### 보이스 필터링 필터를 사용하여 원하는 것을 정확히 찾으세요: | 필터 | 옵션 | |--------|---------| | **Model** | `ssfm-v30` 또는 `ssfm-v21` | | **Gender** | `Male` 또는 `Female` | | **Age** | `Child`, `Teenager`, `Young Adult`, `Middle Age`, `Elder` | | **Use Cases** | `Audiobook`, `Ads`, `E-learning`, `Game`, `Podcast` 등 | Typecast 노드의 음성 필터 옵션 --- ## 문제 해결 - n8n을 완전히 다시 시작하세요 - 브라우저 캐시를 지우세요 - **Settings** → **Community Nodes**에서 설치를 확인하세요 - API 키가 올바른지 확인하세요 - [타입캐스트 API 콘솔](https://studio.typecast.ai/developers/api/)에서 키를 확인하세요 - 키에 여분의 공백이 없는지 확인하세요 - **Get All Voices** 작업을 사용하여 유효한 Voice ID를 찾으세요 - Voice ID는 대소문자를 구분합니다 (소문자 `tc_...` 사용) - 텍스트가 비어 있지 않은지 확인하세요 - API 크레딧이 충분한지 확인하세요 - 노드 출력에서 오류 메시지를 확인하세요 --- ## 참고 자료 npm 레지스트리에서 보기 소스 코드 보기 및 기여하기 사용 가능한 모든 보이스 둘러보기 타입캐스트 API 탐색하기 ## 무음 길이 조절 `@neosapience/n8n-nodes-typecast` **1.2.5 이상**의 음성 생성 작업에서 **Additional Options → Remaining Silence (Ms)**를 추가하세요. 추가 시 표시되는 `300`은 해당 옵션의 초기값이며, 옵션을 추가하지 않은 기존 워크플로에는 적용되지 않습니다. `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. 일반·스트리밍·타임스탬프 TTS는 `output.remove_silence_ms`, Compose는 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 전달합니다. 반환 타임스탬프는 처리 후 오디오를 기준으로 하며, 명시적인 `pause` 세그먼트는 유지됩니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # MCP 이 [Model Context Protocol](https://modelcontextprotocol.io/docs/getting-started/intro)을 연결하여 AI에게 완전한 타입캐스트 지식을 제공하고, 타입캐스트 TTS를 서비스에 몇 분 만에 통합하세요. ## 원클릭 설치 Cursor와 Replit의 경우 아래 버튼을 클릭하세요: Cursor IDE용 원클릭 설치 Replit용 원클릭 설치 ### 기타 MCP 클라이언트 다른 MCP 호환 클라이언트의 경우 이 URL을 추가하세요: ``` https://typecast.ai/docs/mcp ``` ```bash claude mcp add --transport http typecast-helper https://typecast.ai/docs/mcp ``` 1. Windsurf 설정 열기 2. **Cascade** → **MCP Servers**로 이동 3. **"Add Server"** → **"Add Remote MCP Server"** 클릭 4. URL 입력: `https://typecast.ai/docs/mcp` `.vscode/mcp.json`에 추가하세요: ```json { "servers": { "typecast-helper": { "url": "https://typecast.ai/docs/mcp" } } } ``` 이제 AI 어시스턴트에게 타입캐스트 TTS를 프로젝트에 통합하는 것을 도와달라고 요청할 수 있습니다. ### 활용 방법 연결되면 AI 어시스턴트가 다음에 대한 지식을 얻습니다: - **API 통합** - 모든 언어에 대한 코드 예제 얻기 - **보이스 선택** - 용도에 맞는 완벽한 보이스 찾기 - **모범 사례** - 다양한 시나리오에 대한 최적의 설정 배우기 - **문제 해결** - 일반적인 문제에 대한 빠른 해결책 ```plaintext 예시: 빠른 통합 "내 프로젝트에 Typecast TTS를 통합해줘." ``` --- 위 설정이 필요한 전부입니다. 아래 섹션은 고급 사용 사례를 위한 선택 사항입니다. --- ## 고급: 자동 TTS 생성 자동으로 오디오 파일을 생성해야 하나요? 타입캐스트 호스팅 또는 자체 호스팅 API MCP 서버를 사용하면 AI 어시스턴트가 **타입캐스트 API를 직접 호출**하여 필요에 따라 오디오를 생성할 수 있습니다. 배치 처리 및 자동화 워크플로우에 적합합니다. 두 방식 모두 자연어 설명으로 음성을 찾는 `recommend_voices`를 제공합니다. 추천 결과에는 `voice_id`, `voice_name`, `score`만 포함되므로, 지원 모델, 감정, 성별, 나이대, 사용 사례 같은 메타데이터가 필요하면 `get_voice` 또는 `get_voices`를 함께 호출하세요. ### 차별점 | 문서 MCP | 호스팅 API MCP | 자체 호스팅 API MCP | |----------|----------------|----------------------| | 지식 및 가이드 제공 | 타입캐스트 API 직접 호출 | 타입캐스트 API 직접 호출 | | API 키 불필요 | 로컬 설치 불필요 | 내 컴퓨터에서 실행 | | 통합 도움에 적합 | 빠른 자동화에 적합 | 로컬 파일 및 오디오 재생에 적합 | ### 사전 준비 사항 - 타입캐스트 API 키 ([여기서 받기](https://studio.typecast.ai/developers/api/)) - [uv](https://docs.astral.sh/uv/) 패키지 매니저 (자체 호스팅만 해당) ### 설정 사용자 지정 헤더를 지원하는 MCP 클라이언트에 호스팅 Streamable HTTP 엔드포인트를 추가하세요: ```json { "mcpServers": { "typecast": { "url": "https://typecast.ai/docs/mcp", "headers": { "X-API-KEY": "YOUR_API_KEY" } } } } ``` API 키가 없으면 호스팅 서버는 `search_documentation`만 제공합니다. 인증된 요청에서는 타입캐스트 API 도구를 모두 사용할 수 있습니다. 생성된 오디오는 한 시간 후 만료되는 비공개 다운로드 URL로 반환됩니다. `play_audio`는 자체 호스팅에서만 사용할 수 있습니다. 호스팅 서버에서 음성을 복제할 때는 `audio_base64`와 `.mp3` 또는 `.wav` 확장자의 `audio_filename`을 보내세요. `~/Library/Application Support/Claude/claude_desktop_config.json` 편집: ```json { "mcpServers": { "typecast": { "command": "uvx", "args": [ "--from", "git+https://github.com/neosapience/typecast-api-mcp-server.git", "typecast-api-mcp-server" ], "env": { "TYPECAST_API_KEY": "YOUR_API_KEY", "TYPECAST_OUTPUT_DIR": "/Users/yourname/Downloads/typecast_output" } } } } ``` `%APPDATA%\Claude\claude_desktop_config.json` 편집: ```json { "mcpServers": { "typecast": { "command": "uvx", "args": [ "--from", "git+https://github.com/neosapience/typecast-api-mcp-server.git", "typecast-api-mcp-server" ], "env": { "TYPECAST_API_KEY": "YOUR_API_KEY", "TYPECAST_OUTPUT_DIR": "C:\\Users\\yourname\\Downloads\\typecast_output" } } } } ``` `~/.config/Claude/claude_desktop_config.json` 편집: ```json { "mcpServers": { "typecast": { "command": "uvx", "args": [ "--from", "git+https://github.com/neosapience/typecast-api-mcp-server.git", "typecast-api-mcp-server" ], "env": { "TYPECAST_API_KEY": "YOUR_API_KEY", "TYPECAST_OUTPUT_DIR": "/home/yourname/Downloads/typecast_output", "XDG_RUNTIME_DIR": "/run/user/1000" } } } } ``` Linux에서는 오디오 재생을 위해 `XDG_RUNTIME_DIR`이 필요합니다. Cursor의 MCP 설정에 추가하세요: ```json { "mcpServers": { "typecast": { "command": "uvx", "args": [ "--from", "git+https://github.com/neosapience/typecast-api-mcp-server.git", "typecast-api-mcp-server" ], "env": { "TYPECAST_API_KEY": "YOUR_API_KEY", "TYPECAST_OUTPUT_DIR": "/path/to/output" } } } } ``` ```bash # Typecast MCP 서버 추가 claude mcp add --transport stdio \ --env TYPECAST_API_KEY=YOUR_API_KEY \ --env TYPECAST_OUTPUT_DIR=/path/to/output \ typecast -- uvx --from git+https://github.com/neosapience/typecast-api-mcp-server.git typecast-api-mcp-server ``` ### 문제 해결 - 구성이 올바른지 확인하세요 - 애플리케이션을 완전히 다시 시작하세요 - `uv`가 설치되어 있고 PATH에서 사용 가능한지 확인하세요 - API 키가 구성에 올바르게 설정되어 있는지 확인하세요 - [타입캐스트 API](https://studio.typecast.ai/developers/api/)에서 키를 확인하세요 - `XDG_RUNTIME_DIR` 환경 변수를 설정하세요 - 오디오 장치 확인: `aplay -l` --- ## 참고 자료 자체 호스팅 MCP 서버 소스 코드 보기 타입캐스트 API 탐색하기 사용 가능한 음성 둘러보기 사용자 정의 통합 구축하기 ## 무음 길이 조절 호스팅된 **Typecast API MCP 서버**의 일반·스트리밍·타임스탬프 TTS 도구는 선택 인자 `remove_silence_ms`를 지원합니다. 도구 호출 인자에 `"remove_silence_ms": 300`을 지정하세요. 문서 검색용 `/docs/mcp`와 API 실행 서버를 구분하고, 셀프 호스팅 서버는 이 옵션이 포함된 최신 소스로 갱신하세요. `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. 일반·스트리밍·타임스탬프 TTS는 `output.remove_silence_ms`, Compose는 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 전달합니다. 반환 타임스탬프는 처리 후 오디오를 기준으로 하며, 명시적인 `pause` 세그먼트는 유지됩니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Pipecat [Pipecat](https://github.com/pipecat-ai/pipecat)은 실시간 멀티모달 AI 음성 에이전트를 구축하기 위한 오픈 소스 프레임워크입니다. Typecast TTS 통합을 사용하면 감정 제어가 가능한 고품질 뉴럴 음성을 음성 AI 파이프라인에 추가할 수 있습니다. ## Pipecat이란? Pipecat은 음성 AI 애플리케이션 구축을 단순화하는 Python 프레임워크입니다. 다양한 서비스(음성-텍스트 변환, LLM, 텍스트-음성 변환)를 통합 파이프라인으로 연결하여 실시간 오디오 스트리밍, 턴테이킹, 전송 프로토콜의 복잡성을 처리합니다. 일반적인 Pipecat 파이프라인은 다음과 같습니다: ``` 사용자 오디오 → STT → LLM → TTS → 봇 오디오 ``` Typecast TTS 서비스(`pipecat-ai-typecast`)는 이 파이프라인에 원활하게 통합되어 LLM 응답을 표현력 있는 음성으로 변환합니다. --- ## 할 수 있는 것 Typecast Pipecat 통합을 사용하면 다음을 할 수 있습니다: - 자연스럽고 표현력 있는 음성으로 **음성 AI 에이전트 구축** - 다양한 성별, 나이, 스타일의 **600+개 음성 중 선택** - **감정 적용** (happy, sad, angry, whisper 등) - 문맥 인식 음성 합성을 위한 **스마트 이모션 사용** - 어디서든 **배포** - Daily, Twilio, 또는 네이티브 WebRTC --- ## 사전 요구 사항 시작하기 전에 다음을 준비하세요: | 요구 사항 | 버전 | |-------------|---------| | Python | 3.10+ | | Pipecat | v0.0.94+ | | 타입캐스트 API 키 | [여기서 받기](https://studio.typecast.ai/developers/api/) | --- ## 설치 Pipecat용 Typecast TTS 서비스를 설치하세요: ```bash pip install pipecat-ai-typecast ``` uv를 사용하시나요? 대신 `uv add pipecat-ai-typecast`를 실행하세요. --- ## 빠른 시작 Pipecat 파이프라인에 Typecast TTS를 통합하는 최소 예제입니다: ```python import os import aiohttp from pipecat.pipeline.pipeline import Pipeline from pipecat_typecast import TypecastTTSService async with aiohttp.ClientSession() as session: # Typecast TTS 초기화 tts = TypecastTTSService( aiohttp_session=session, api_key=os.getenv("TYPECAST_API_KEY"), voice_id=os.getenv("TYPECAST_VOICE_ID", "tc_672c5f5ce59fac2a48faeaee"), ) # 파이프라인 구축 pipeline = Pipeline([ transport.input(), # 사용자 오디오 입력 stt, # 음성-텍스트 변환 context_aggregator.user(), # 문맥에 사용자 텍스트 추가 llm, # LLM이 응답 생성 tts, # Typecast TTS 합성 transport.output(), # 사용자에게 오디오 스트리밍 context_aggregator.assistant(), # 어시스턴트 응답 저장 ]) ``` 환경 변수를 설정하세요: - `TYPECAST_API_KEY` - 타입캐스트 API 키 (필수) - `TYPECAST_VOICE_ID` - 사용할 음성 (선택 사항, 기본 음성으로 설정됨) --- ## 구성 `TypecastTTSService`는 프리셋 기반 및 문맥 인식 감정 제어를 모두 지원합니다. ### 기본 구성 ```python from pipecat_typecast import TypecastTTSService tts = TypecastTTSService( aiohttp_session=session, api_key=os.getenv("TYPECAST_API_KEY"), voice_id="tc_672c5f5ce59fac2a48faeaee", model="ssfm-v30", # 최신 모델 (기본값) ) ``` ### 이모션 프리셋 제어 일관된 음성 스타일링을 위해 미리 정의된 감정 중에서 선택하세요: ```python from pipecat_typecast import ( TypecastTTSService, TypecastInputParams, PresetPromptOptions, OutputOptions, ) params = TypecastInputParams( prompt_options=PresetPromptOptions( emotion_preset="happy", # normal | happy | sad | angry | whisper | toneup | tonedown emotion_intensity=1.3, # 0.0 - 2.0 ), output_options=OutputOptions( volume=110, # 0 - 200 (퍼센트) audio_pitch=2, # -12 ~ 12 (반음) audio_tempo=1.05, # 0.5 - 2.0 (재생 속도) ), ) tts = TypecastTTSService( aiohttp_session=session, api_key=os.getenv("TYPECAST_API_KEY"), params=params, ) ``` ### 스마트 이모션 (문맥 인식) AI가 주변 텍스트에서 감정을 자동으로 추론하도록 합니다: ```python from pipecat_typecast import ( TypecastTTSService, TypecastInputParams, SmartPromptOptions, ) params = TypecastInputParams( prompt_options=SmartPromptOptions( previous_text="방금 정말 좋은 소식을 들었어요!", # 최대 2000자 next_text="모두와 공유하고 싶어서 기다려지지 않아요!", ), ) tts = TypecastTTSService( aiohttp_session=session, api_key=os.getenv("TYPECAST_API_KEY"), params=params, ) ``` 7가지 감정 중에서 수동으로 선택합니다: Normal, Happy, Sad, Angry, Whisper, Tone Up, Tone Down. 일관된 음성 스타일링에 적합합니다. AI가 텍스트 맥락에서 최적의 감정을 자동으로 감지합니다. 자연스러운 대화에 적합합니다. ### 매개변수 참조 | 매개변수 | 범위 | 설명 | |-----------|-------|-------------| | `emotion_preset` | 음성에 따라 다름 | ssfm-v30: `normal`, `happy`, `sad`, `angry`, `whisper`, `toneup`, `tonedown` | | `emotion_intensity` | 0.0 - 2.0 | 1.0 이상의 값은 표현력을 증가시킴 | | `audio_pitch` | -12 ~ 12 | 반음 조절 | | `audio_tempo` | 0.5 - 2.0 | 권장: 0.85 - 1.15 | | `volume` | 0 - 200 | 퍼센트로 표시되는 오디오 볼륨 | | `seed` | uint32 | 결정적 합성을 위한 부호 없는 정수 시드 (≥ 0) | --- ## 지원 전송 프로토콜 Pipecat은 여러 전송 프로토콜을 지원합니다. Typecast는 모든 프로토콜에서 작동합니다: [Daily](https://www.daily.co/)는 WebRTC 기반 비디오 및 오디오 인프라를 제공합니다. ```python from pipecat.transports.daily.transport import DailyParams transport_params = DailyParams( audio_in_enabled=True, audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ) ``` [Twilio](https://www.twilio.com/)는 전화 네트워크를 통한 음성 통화를 가능하게 합니다. ```python from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams transport_params = FastAPIWebsocketParams( audio_in_enabled=True, audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ) ``` 브라우저 기반 애플리케이션을 위한 네이티브 WebRTC. ```python from pipecat.transports.base_transport import TransportParams transport_params = TransportParams( audio_in_enabled=True, audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ) ``` --- ## 전체 예제 음성 AI 에이전트를 생성하는 완전한 작동 예제입니다: ```python import os import aiohttp from dotenv import load_dotenv from pipecat.audio.vad.silero import SileroVADAnalyzer from pipecat.pipeline.pipeline import Pipeline from pipecat.pipeline.runner import PipelineRunner from pipecat.pipeline.task import PipelineParams, PipelineTask from pipecat.processors.aggregators.llm_context import LLMContext from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair from pipecat.services.deepgram.stt import DeepgramSTTService from pipecat.services.openai.llm import OpenAILLMService from pipecat.transports.daily.transport import DailyParams, DailyTransport from pipecat_typecast import TypecastTTSService load_dotenv() async def main(): async with aiohttp.ClientSession() as session: # 서비스 초기화 stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY")) llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY")) tts = TypecastTTSService( aiohttp_session=session, api_key=os.getenv("TYPECAST_API_KEY"), ) # 대화 문맥 설정 messages = [ { "role": "system", "content": "당신은 유용한 AI 어시스턴트입니다. 응답은 간결하게 해주세요.", }, ] context = LLMContext(messages) context_aggregator = LLMContextAggregatorPair(context) # 전송 구성 transport = DailyTransport( room_url=os.getenv("DAILY_ROOM_URL"), token=os.getenv("DAILY_TOKEN"), params=DailyParams( audio_in_enabled=True, audio_out_enabled=True, vad_analyzer=SileroVADAnalyzer(), ), ) # 파이프라인 구축 및 실행 pipeline = Pipeline([ transport.input(), stt, context_aggregator.user(), llm, tts, transport.output(), context_aggregator.assistant(), ]) task = PipelineTask(pipeline, params=PipelineParams()) runner = PipelineRunner() await runner.run(task) if __name__ == "__main__": import asyncio asyncio.run(main()) ``` --- ## 레거시 모델 (ssfm-v21) 레거시 ssfm-v21 모델을 사용해야 하는 경우: ```python from pipecat_typecast import ( TypecastTTSService, TypecastInputParams, PromptOptions, ) params = TypecastInputParams( prompt_options=PromptOptions( emotion_preset="happy", # normal | happy | sad | angry emotion_intensity=1.3, ), ) tts = TypecastTTSService( aiohttp_session=session, api_key=os.getenv("TYPECAST_API_KEY"), model="ssfm-v21", params=params, ) ``` 참고: ssfm-v21은 더 적은 감정 프리셋을 지원합니다 (`whisper`, `toneup`, `tonedown` 없음). --- ## 문제 해결 - `TYPECAST_API_KEY` 환경 변수가 설정되어 있는지 확인하세요 - [타입캐스트 API 콘솔](https://studio.typecast.ai/developers/api/)에서 키를 확인하세요 - 키에 여분의 공백이 없는지 확인하세요 - 전송이 `audio_out_enabled=True`로 구성되어 있는지 확인하세요 - TTS 서비스가 파이프라인에 포함되어 있는지 확인하세요 - API 키에 충분한 크레딧이 있는지 확인하세요 - 권장 범위(0.85 - 1.15) 내에서 `audio_tempo`를 조절하세요 - 다른 `emotion_intensity` 값을 시도하세요 - 샘플 레이트가 전송 구성과 일치하는지 확인하세요 - `pipecat-typecast`가 아닌 `pipecat-ai-typecast`를 설치했는지 확인하세요 - Python 버전이 3.10 이상인지 확인하세요 - Pipecat 버전이 v0.0.94 이상인지 확인하세요 --- ## 리소스 소스 코드 및 예제 pip로 설치 Pipecat에 대해 더 알아보기 사용 가능한 음성 둘러보기 ## 무음 길이 조절 `pipecat-ai-typecast` **0.3.1 이상**에서 일반·스트리밍 TTS 모두 지원합니다. ```python from pipecat_typecast import TypecastInputParams, OutputOptions params = TypecastInputParams( output_options=OutputOptions(remove_silence_ms=300), ) ``` `TypecastTTSService(..., params=params)`로 전달하세요. `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. `TypecastTTSService`는 일반·스트리밍 TTS의 `output.remove_silence_ms`로 이 설정을 전달합니다. 이 연동은 타임스탬프 TTS나 Compose를 제공하지 않습니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # LlamaIndex [LlamaIndex](https://www.llamaindex.ai/)는 LLM 애플리케이션을 구축하기 위한 강력한 데이터 프레임워크입니다. Typecast 도구 연동을 통해 AI 에이전트가 감정 조절이 가능한 표현력 있는 음성을 생성할 수 있습니다. ## LlamaIndex란? LlamaIndex는 컨텍스트 기반 LLM 애플리케이션을 구축하기 위한 Python 프레임워크입니다. 데이터 수집, 인덱싱, 쿼리 도구와 외부 도구를 사용할 수 있는 에이전트 기능을 제공합니다. Typecast 도구를 사용하면 LlamaIndex 에이전트에서: - **텍스트로부터 음성 생성** - 다양한 목소리로 커스터마이징 가능 - **감정 조절** - 행복, 슬픔, 화남, 속삭임 등 다양한 감정 표현 - **목소리 탐색** - 모델, 성별, 연령, 용도별 필터링 - **재현 가능한 오디오 생성** - seed 파라미터 활용 --- ## 사전 요구사항 시작하기 전에 다음을 준비하세요: | 요구사항 | 버전 | |----------|------| | Python | 3.11+ | | LlamaIndex Core | 0.13–0.14 | | 타입캐스트 API 키 | [여기서 발급받기](https://studio.typecast.ai/developers/api/) | --- ## 설치 LlamaIndex용 Typecast 도구를 설치합니다: ```bash pip install llama-index-tools-typecast ``` 에이전트 사용을 위해 LLM 프로바이더도 설치하세요: `pip install llama-index-llms-openai` --- ## 빠른 시작 LlamaIndex 에이전트에서 Typecast TTS를 사용하는 간단한 예제입니다: ```python from llama_index.tools.typecast import TypecastToolSpec from llama_index.core.agent.workflow import FunctionAgent from llama_index.llms.openai import OpenAI # Typecast 도구 초기화 speech_tool = TypecastToolSpec(api_key="your-typecast-key") # Typecast 기능을 가진 에이전트 생성 agent = FunctionAgent( tools=speech_tool.to_tool_list(), llm=OpenAI(model="gpt-4o-mini"), ) # 에이전트를 통해 음성 생성 result = await agent.run( 'Create speech from the text "Hello world!" with a happy emotion ' 'and output the file to "speech.wav"' ) print(result) ``` 환경 변수 설정: - `OPENAI_API_KEY` - OpenAI API 키 (에이전트의 LLM용) - 타입캐스트 API 키는 `TypecastToolSpec` 생성자에 직접 전달 --- ## 제공되는 도구 `TypecastToolSpec`은 에이전트에서 사용할 수 있는 세 가지 도구를 제공합니다: 텍스트를 음성으로 변환. 감정, 피치, 템포 조절 및 재현 가능한 결과 생성. 선택적 필터링과 함께 사용 가능한 모든 Typecast 목소리 목록 조회. ID로 특정 목소리의 상세 정보 조회. --- ## 직접 사용 (에이전트 없이) 더 세밀한 제어가 필요한 경우 도구를 직접 사용할 수 있습니다: ### 목소리 탐색 ```python from llama_index.tools.typecast import TypecastToolSpec speech_tool = TypecastToolSpec(api_key="your-typecast-key") # 선택적 필터로 사용 가능한 목소리 조회 voices = speech_tool.get_voices( model="ssfm-v30", gender="female", age="young_adult", use_case="Audiobook" ) print(f"{len(voices)}개의 목소리를 찾았습니다") for voice in voices: print(f"{voice['voice_name']} ({voice['voice_id']})") ``` ### 목소리 상세 정보 조회 ```python # 특정 목소리 정보 조회 voice = speech_tool.get_voice("tc_62a8975e695ad26f7fb514d1") print(f"목소리: {voice['voice_name']}") print(f"성별: {voice.get('gender')}, 연령: {voice.get('age')}") print(f"용도: {voice.get('use_cases')}") # 모델별 지원 감정 확인 for model in voice["models"]: print(f"모델 {model['version']}: 감정 = {model['emotions']}") ``` ### 음성 생성 ```python # 모든 파라미터를 사용한 텍스트-음성 변환 output_path = speech_tool.text_to_speech( text="안녕하세요! 테스트입니다.", voice_id="tc_62a8975e695ad26f7fb514d1", output_path="speech.wav", model="ssfm-v30", language="kor", emotion_preset="happy", emotion_intensity=1.5, volume=100, audio_pitch=0, audio_tempo=1.0, audio_format="wav", seed=42, # 부호 없는 정수 시드 (재현 가능한 결과) ) print(f"오디오 저장 완료: {output_path}") ``` --- ## 기능 ### 다양한 음성 모델 Typecast는 여러 AI 음성 모델 버전을 지원합니다: | 모델 | 설명 | |------|------| | `ssfm-v30` | 향상된 감정 표현이 가능한 최신 모델 (권장) | | `ssfm-v21` | 하위 호환성을 위한 레거시 모델 | ### 감정 조절 생성되는 음성의 감정 표현을 조절할 수 있습니다: | 감정 | ssfm-v30 | ssfm-v21 | |------|----------|----------| | `normal` | ✓ | ✓ | | `happy` | ✓ | ✓ | | `sad` | ✓ | ✓ | | `angry` | ✓ | ✓ | | `whisper` | ✓ | - | | `toneup` | ✓ | - | | `tonedown` | ✓ | - | `emotion_intensity` (0.0 - 2.0)로 표현력을 조절합니다. 1.0보다 큰 값은 강도를 높입니다. ### 다국어 지원 Typecast는 27개 이상의 언어를 지원합니다: - 영어 (`eng`) - 한국어 (`kor`) - 일본어 (`jpn`) - 중국어 (`zho`) - 스페인어 (`spa`) - 그 외 다수... ### 오디오 커스터마이징 오디오 출력을 세밀하게 조정할 수 있습니다: | 파라미터 | 범위 | 설명 | |----------|------|------| | `volume` | 0 - 200 | 오디오 볼륨 (백분율) | | `audio_pitch` | -12 ~ 12 | 반음 단위 조정 | | `audio_tempo` | 0.5 - 2.0 | 재생 속도 (권장: 0.85 - 1.15) | | `audio_format` | `wav`, `mp3` | 출력 형식 | | `seed` | uint32 | 재현 가능한 오디오 생성을 위한 부호 없는 정수 시드 (≥ 0) | --- ## 전체 에이전트 예제 목소리를 탐색하고 음성을 생성하는 에이전트의 전체 예제입니다: ```python import os from llama_index.tools.typecast import TypecastToolSpec from llama_index.core.agent.workflow import FunctionAgent from llama_index.llms.openai import OpenAI # API 키 설정 os.environ["OPENAI_API_KEY"] = "your-openai-key" # Typecast 도구 초기화 speech_tool = TypecastToolSpec(api_key="your-typecast-key") # Typecast 기능을 가진 에이전트 생성 agent = FunctionAgent( tools=speech_tool.to_tool_list(), llm=OpenAI(model="gpt-4o-mini"), ) # 에이전트가 목소리를 탐색하고 음성을 생성하도록 요청 result = await agent.run( 'Get the list of available voices, select the first female voice, ' 'and use it to create speech from the text "Welcome to Typecast!" ' 'with a happy emotion, saving to "welcome.wav"' ) print(result) ``` --- ## 문제 해결 - `TypecastToolSpec`에 올바른 API 키를 전달했는지 확인하세요 - [타입캐스트 API 콘솔](https://studio.typecast.ai/developers/api/)에서 키를 확인하세요 - 키에 불필요한 공백이 있는지 확인하세요 - 출력 경로에 쓰기 권한이 있는지 확인하세요 - API 키에 충분한 크레딧이 있는지 확인하세요 - voice_id가 유효한지 확인하세요 - `llama-index-tools-typecast`를 설치했는지 확인하세요 - 에이전트 사용 시 `llama-index-llms-openai` 또는 선호하는 LLM 프로바이더도 설치하세요 - Python 버전이 3.11 이상인지 확인하세요 - `llama-index-core` 버전이 0.13 또는 0.14인지 확인하세요 - 에이전트에게 원하는 작업을 구체적으로 프롬프트하세요 - 복잡한 작업은 간단한 단계로 나누세요 - 오디오 파일의 출력 경로 예시를 제공하세요 --- ## 리소스 소스 코드 및 예제 LlamaHub에서 보기 LlamaIndex 더 알아보기 사용 가능한 목소리 탐색 --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Postman 코드를 작성하지 않고 공개 Postman 컬렉션으로 타입캐스트 API 엔드포인트를 빠르게 테스트하세요. ## 시작하기 [Typecast Developers Postman 컬렉션](https://www.postman.com/typecast-api-team/typecast-developers/overview)을 방문하세요. **"Fork"** 를 클릭하여 컬렉션을 Postman 워크스페이스에 추가하세요. 포크한 컬렉션에서 `X-API-KEY` 헤더에 API 키를 설정하세요: ``` X-API-KEY: YOUR_API_KEY ``` 어떤 엔드포인트에든 요청을 보내고 실시간으로 응답을 확인하세요. --- ## 리소스 모든 타입캐스트 API 엔드포인트 탐색하기 타입캐스트 API 키 만들기 --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 콘텐츠 최신 제품 업데이트 소식은 물론, 누구나 바로 적용해 효율을 높일 수 있는 실제 활용 사례와 가이드를 담았습니다.

2026년 7월 8일

자연스러운 한국어 음성 에이전트(콜봇) 만들기 - Pipecat, 타입캐스트 연동

Pipecat에 타입캐스트 TTS를 연결해 스트리밍과 감정 제어가 가능한 한국어 음성 에이전트를 만드는 방법입니다.

2026년 7월 2일

쇼츠 나레이션, 코딩 없이 자동화하는 법 - 타입캐스트 CLI

타입캐스트 CLI로 cast 명령어 한 줄에 음성·자막 생성과 자동화 연동을 구성하는 방법입니다.

2026년 6월 24일

TTS가 전화번호·금액을 어색하게 읽는다면

전화번호, 금액, 날짜처럼 숫자와 기호가 섞인 문장을 autotag로 자연스럽게 읽게 만드는 API 연동 가이드입니다.

2026년 6월 18일

마음에 든 쇼츠 목소리, 링크로 가장 비슷한 AI 보이스 찾는 법

쇼츠 보이스 파인더로 원하는 톤과 가까운 AI 보이스를 찾는 방법입니다.

2026년 6월 11일

보이스 클로닝, 5초면 됩니다 - TTS API 활용법

짧은 음성 샘플로 커스텀 보이스를 만들고 API에서 활용하는 방법입니다.

2026년 5월 22일

유튜브 자막 자동화, 타임스탬프 TTS API로 끝냈습니다

음성과 자막 타이밍을 한 번에 만드는 흐름입니다.

2026년 5월 15일

Claude Skill 한 줄 설치로 AI음성 API 연동하기

Skill로 에이전트 도구에 타입캐스트 API를 연결합니다.

2026년 4월 24일

AI 챗봇 음성 지연, 스트리밍 TTS API로 해결하세요

실시간 음성 UX에서 TTFB가 중요한 이유를 설명합니다.

2026년 4월 17일

타입캐스트 API SSFM 3.0 신기능 써보셨나요?

Smart Emotion과 감정 표현 업데이트를 살펴봅니다.

2026년 3월 25일

개발자 없이도 숏츠 자동화가 된다고요? n8n으로 영상 콘텐츠 자동화하는 법

n8n으로 영상 콘텐츠 자동화 흐름을 구성합니다.

2026년 2월 26일

콘텐츠 제작 자동화의 마지막 퍼즐 , n8n·클로드 코드 그리고 음성

콘텐츠 자동화 흐름에서 음성이 맡는 역할을 다룹니다.

--- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 개요 타입캐스트 명령어 한 줄만 저장해두면, 같은 방식의 음성을 언제든 다시 만들 수 있습니다. 직접 실행해도 되고, 메모에 남겨두어도 되고, AI 에이전트에게 전달해서 매번 같은 설정으로 음성 파일과 자막을 만들게 할 수도 있습니다. 반복해서 콘텐츠를 만들 때마다 같은 타입캐스트 요청을 처음부터 다시 구성할 필요가 없습니다. 한두 개의 명령어만 익혀두면 초안 음성, 내레이션, 보이스 미리듣기, 자막 생성, 에이전트 기반 제작 작업을 명령어 단위로 재사용할 수 있습니다. ```bash cast "Hello, world!" cast "Hello, world!" --voice-id tc_xxx --out hello.wav cast "Hello, world." --out hello.wav --timestamp-out hello.srt cast "I just got promoted!" --emotion smart ``` Homebrew 또는 Go로 설치하고 타입캐스트 API 키로 인증합니다. 모델, 보이스, 감정, 출력 파일 옵션을 조합해 음성을 생성합니다. 오디오와 함께 타임스탬프 JSON, SRT, WebVTT 자막을 생성합니다. CLI에서 보이스를 조회, 미리듣기, 선택, 랜덤 추출할 수 있습니다. WAV 또는 MP3 샘플로 커스텀 보이스를 만들고 CLI에서 사용합니다. 설정 파일, 환경 변수, 플래그로 CLI 기본값을 관리합니다. ## 반복할 수 있는 일 | 워크플로우 | 명령어 | |------------|--------| | 음성 바로 재생 | `cast "Hello, world!"` | | WAV 파일 저장 | `cast "Hello, world!" --out hello.wav` | | MP3 파일 저장 | `cast "Hello, world!" --out hello.mp3 --format mp3` | | SRT 자막 생성 | `cast "Hello, world." --out hello.wav --timestamp-out hello.srt` | | WebVTT 자막 생성 | `cast "Hello, world." --out hello.wav --timestamp-out hello.vtt --timestamp-format vtt` | | 스마트 이모션 사용 | `cast "I can't believe it!" --emotion smart` | | 보이스 인터랙티브 선택 | `cast voices pick` | | 커스텀 보이스 클로닝 | `cast voices clone sample.wav --name "My Clone"` | | 기본 보이스 설정 | `cast config set voice-id tc_xxx` | CLI 사용에는 타입캐스트 API 키가 필요합니다. [타입캐스트 API 콘솔](https://studio.typecast.ai/developers/api)에서 키를 발급받으세요. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 설치 ## 설치 ```bash brew install neosapience/tap/cast ``` ```bash go install github.com/neosapience/cast@latest ``` ## 로그인 로그인 명령을 실행한 뒤 타입캐스트 API 키를 입력합니다: ```bash cast login ``` 키를 직접 전달할 수도 있습니다: ```bash cast login ``` API 키는 [타입캐스트 API 콘솔](https://studio.typecast.ai/developers/api)에서 발급받을 수 있습니다. ## 설치 확인 ```bash cast "Hello, world!" ``` 오디오가 재생되면 CLI를 사용할 준비가 끝난 것입니다. 로컬 오디오 재생 대신 파일 생성을 확인하려면 `cast "Hello, world!" --out hello.wav`를 실행하세요. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 음성 생성 ## 기능 맵 | 필요 | 사용 | |------|------| | 즉시 로컬 재생 | `cast "text"` | | 재사용 가능한 오디오 파일 | `--out file.wav` 또는 `--out file.mp3 --format mp3` | | 에이전트의 실시간 느낌 응답 | `--out` 없이 기본 재생 | | 타임스탬프 JSON | `--timestamp-out file.json` | | SRT 또는 WebVTT 자막 | `--timestamp-out file.srt` 또는 `--timestamp-out file.vtt` | | 클로닝한 커스텀 보이스 | `cast voices clone` 후 `--voice-id uc_xxx` | ## 기본 사용법 ```bash # 바로 재생 cast "Hello, world!" # 특정 보이스 사용 cast "Hello, world!" --voice-id tc_xxx # WAV 파일로 저장 cast "Hello, world!" --out hello.wav # MP3 파일로 저장 cast "Hello, world!" --out hello.mp3 --format mp3 # 오디오와 SRT 자막 함께 저장 cast "Hello, world. This is a test." --out hello.wav --timestamp-out hello.srt ``` 기본적으로 `cast`는 오디오를 즉시 재생합니다. `--out`을 사용하면 WAV 또는 MP3 파일로 저장할 수 있습니다. 로컬 에이전트가 빠르게 말해야 하는 상황에서는 `--out` 없이 바로 재생하는 방식이 가장 단순합니다. API 레벨의 chunked streaming(`POST /v1/text-to-speech/stream`)은 [Streaming TTS](/ko/quickstart#stream-audio-in-real-time)와 SDK 문서를 참고하세요. ## 옵션 | 플래그 | 설명 | 기본값 | |--------|------|--------| | `--voice-id` | Voice ID | `tc_60e5426de8b95f1d3000d7b5` | | `--model` | 모델 (`ssfm-v30`, `ssfm-v21`) | `ssfm-v30` | | `--language` | 언어 코드 (ISO 639-3) | 자동 감지 | | `--emotion` | 감정 유형: `smart`, `preset` | | | `--emotion-preset` | 이모션 프리셋 (`--emotion preset` 필요) | | | `--emotion-intensity` | 감정 강도 0.0-2.0 (`--emotion preset` 필요) | `1.0` | | `--prev-text` | 문맥을 위한 이전 문장 (`--emotion smart` 전용) | | | `--next-text` | 문맥을 위한 다음 문장 (`--emotion smart` 전용) | | | `--volume` | 볼륨 (0-200) | `100` | | `--pitch` | 피치 (반음 단위, -12 ~ +12) | `0` | | `--tempo` | 템포 배율 (0.5-2.0) | `1.0` | | `--remove-silence-ms` | 남길 무음 길이(정수 0–1000ms). 0은 검출된 무음 제거 | 미설정 | | `--format` | 출력 형식 (`wav`, `mp3`) | `wav` | | `--seed` | 재현 가능한 출력을 위한 부호 없는 정수 시드 (`>= 0`) | | | `--out` | 재생 대신 파일로 저장 | | | `--timestamp-out` | 타임스탬프 출력을 JSON, SRT, WebVTT로 저장 | | | `--timestamp-format` | 타임스탬프 출력 형식 (`json`, `srt`, `vtt`) | `--timestamp-out`에서 추론 | | `--timestamp-granularity` | 타임스탬프 단위 (`word`, `char`, `both`) | 서버 기본값 | ## 모델 | 모델 | 언어 | 감정 | 지연시간 | |------|------|------|----------| | `ssfm-v30` | 35+개 | 7개 프리셋 + 스마트 이모션 | 표준 | | `ssfm-v21` | 27개 | 4개 프리셋: normal, happy, sad, angry | 낮음 | ```bash cast "Hello, world!" --model ssfm-v21 ``` ## 감정 AI가 텍스트에서 적절한 감정을 자동으로 추론합니다. 스마트 이모션은 `ssfm-v30`에서 사용할 수 있습니다. ```bash cast "I just got promoted!" --emotion smart ``` 더 나은 문맥을 위해 앞뒤 문장을 제공할 수 있습니다: ```bash cast "I just got promoted!" --emotion smart \ --prev-text "I have been working so hard this year." \ --next-text "Let's celebrate tonight!" ``` `--emotion-preset`으로 특정 감정을 선택하고 `--emotion-intensity`로 강도를 제어합니다. | 모델 | 사용 가능한 프리셋 | |------|-------------------| | `ssfm-v30` | `normal`, `happy`, `sad`, `angry`, `whisper`, `toneup`, `tonedown` | | `ssfm-v21` | `normal`, `happy`, `sad`, `angry` | ```bash cast "Hello, world!" --emotion preset --emotion-preset happy cast "Hello, world!" --emotion preset --emotion-preset happy --emotion-intensity 2.0 cast "Hello, world!" --emotion preset --emotion-preset whisper --emotion-intensity 0.5 cast "Hello, world!" --model ssfm-v21 --emotion preset --emotion-preset sad ``` ## 무음 길이 조절 **Cast v1.0.10 이상**에서 `--remove-silence-ms`로 설정합니다. 기본값은 미설정이며 `0`이 아닙니다. ```bash cast "Hello. Thank you for listening." --voice-id tc_672c5f5ce59fac2a48faeaee --remove-silence-ms 300 ``` `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. 일반·스트리밍·타임스탬프 TTS는 `output.remove_silence_ms`, Compose는 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 전달합니다. 반환 타임스탬프는 처리 후 오디오를 기준으로 하며, 명시적인 `pause` 세그먼트는 유지됩니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 타임스탬프와 자막 CLI는 타입캐스트 Timestamp TTS를 호출해 생성된 오디오와 함께 정렬 데이터를 저장할 수 있습니다. 쇼츠 자막, 소셜 영상 캡션 타이밍, 가라오케 스타일 하이라이트, 립싱크 메타데이터가 필요할 때 사용합니다. ## 자막 생성 ```bash # 오디오와 SRT 자막 저장 cast "Hello, world. This is a test." \ --out hello.wav \ --timestamp-out hello.srt # 오디오와 WebVTT 자막 저장 cast "Hello, world. This is a test." \ --out hello.wav \ --timestamp-out hello.vtt \ --timestamp-format vtt ``` `--timestamp-format`을 생략하면 CLI는 `--timestamp-out` 확장자에서 `srt` 또는 `vtt`를 추론하고, 그렇지 않으면 `json`으로 저장합니다. ## 원본 타임스탬프 JSON 저장 ```bash cast "Hello, world. This is a test." \ --out hello.wav \ --timestamp-out hello.timestamps.json ``` JSON은 다른 도구가 자막을 만들거나, 텍스트 애니메이션을 렌더링하거나, 시각 요소를 직접 정렬해야 할 때 유용합니다. ## Granularity 선택 ```bash cast "Hello, world." \ --out hello.wav \ --timestamp-out hello.srt \ --timestamp-granularity both ``` 일본어(`jpn`)나 중국어(`zho`)처럼 단어 사이 공백이 없는 언어는 문자 단위 타임스탬프가 자막 타이밍에 더 적합합니다: ```bash cast "こんにちは。世界。" \ --language jpn \ --out hello.wav \ --timestamp-out hello.srt ``` ## 에이전트용 자막 워크플로우 ```text script.txt에서 내레이션 오디오와 자막을 만들어줘. CLI를 사용해줘. 오디오는 ./video/voiceover.wav에 저장해줘. 자막은 ./video/voiceover.srt에 저장해줘. 자막 파일은 오디오 파일 옆에 둬. ``` ## 출력 선택 | 출력 | 사용 시점 | |------|-----------| | `.srt` | 영상 편집기, Shorts/Reels/TikTok 자막 import | | `.vtt` | 웹 비디오 플레이어와 브라우저 기반 preview | | `.json` | 커스텀 렌더링, 가라오케 하이라이트, 립싱크, 후속 자동화 | 소셜 영상에서는 오디오와 자막을 같은 단계에서 생성하세요. 최종 내레이션과 자막 타이밍이 같은 합성 결과에 묶입니다. ## 무음 길이 조절 **Cast v1.0.10 이상**에서 `--remove-silence-ms`로 설정합니다. 기본값은 미설정이며 `0`이 아닙니다. ```bash cast "Hello. Thank you for listening." --voice-id tc_672c5f5ce59fac2a48faeaee --remove-silence-ms 300 ``` `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. 일반·스트리밍·타임스탬프 TTS는 `output.remove_silence_ms`, Compose는 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 전달합니다. 반환 타임스탬프는 처리 후 오디오를 기준으로 하며, 명시적인 `pause` 세그먼트는 유지됩니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 보이스 보이스 명령으로 기본 제공 보이스를 찾거나 기본 보이스를 저장하고, 프로젝트용 커스텀 클로닝 보이스를 만들 수 있습니다. 클로닝 전용 예시는 [보이스 클로닝](/ko/cli-reference/voice-cloning)을 참고하세요. ## 인터랙티브 피커 보이스를 인터랙티브하게 탐색, 미리듣기, 선택할 수 있습니다: ```bash cast voices pick cast voices pick --gender female --age young_adult cast voices pick --text "미리듣기 문장" ``` | 키 | 동작 | |----|------| | **P** | 현재 모델/감정 프리셋으로 미리듣기 | | **E** | 스마트 이모션으로 미리듣기 (`ssfm-v30` 전용) | | **S** | 기본 보이스로 설정 | | **C** | 보이스 ID를 클립보드에 복사 | | **Enter** | 확인 후 보이스 ID 출력 | | **Esc** | 뒤로가기 | ## 토너먼트 일대일 토너먼트 방식으로 가장 마음에 드는 보이스를 찾을 수 있습니다: ```bash cast voices tournament cast voices tournament --gender female --size 16 cast voices tournament --text "미리듣기 문장" ``` | 키 | 동작 | |----|------| | **P** | 보이스 1 미리듣기 | | **Q** | 보이스 2 미리듣기 | | **1** | 보이스 1 선택 | | **2** | 보이스 2 선택 | ## 랜덤 보이스 실험용으로 랜덤 보이스를 선택합니다: ```bash cast voices random cast voices random --gender female --age young_adult cast "Hello!" --voice-id $(cast voices random --model ssfm-v30 --gender female) ``` ## 목록 및 상세 조회 필터를 사용하여 보이스를 조회합니다: ```bash cast voices list cast voices list --gender female cast voices list --age young_adult cast voices list --model ssfm-v30 cast voices list --use-case Audiobook cast voices list --json ``` 사용 가능한 용도: `Announcer`, `Anime`, `Audiobook`, `Conversational`, `Documentary`, `E-learning`, `Rapper`, `Game`, `Tiktok/Reels`, `News`, `Podcast`, `Voicemail`, `Ads` 특정 보이스의 상세 정보를 조회합니다: ```bash cast voices get ``` 텍스트 설명으로 보이스를 추천받습니다: ```bash cast voices recommend "warm female voice for product tutorials" cast voices recommend "calm narrator for meditation" --count 5 --json ``` 추천 결과에는 `voice_id`, `voice_name`, `score`만 포함됩니다. 지원 모델, 감정, 성별, 나이대, 사용 사례 같은 메타데이터가 필요하면 `cast voices get ` 또는 `cast voices list`를 함께 사용하세요. ## 보이스 클로닝 ```bash cast voices clone sample.wav --name "My Clone" cast "Hello from my cloned voice." --voice-id uc_xxx --out cloned.wav cast voices delete uc_xxx ``` 프로젝트 전용 리뷰 보이스, 내레이션 초안, 반복 콘텐츠 생성에는 클로닝한 보이스를 사용할 수 있습니다. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 보이스 클로닝 Cast CLI v1.0.9 이상에서는 로컬 WAV 또는 MP3 샘플로 퀵 클로닝과 프리미엄 클로닝을 모두 사용할 수 있습니다. | 방식 | 적합한 용도 | 처리 방식 | |------|-------------|-----------| | 퀵 클로닝 | 빠른 미리보기와 임시 프로젝트 보이스 | 즉시 사용 가능한 보이스 ID 반환 | | 프리미엄 클로닝 | 고품질 프로덕션 보이스 | 비동기 학습 후 상태 확인 필요 | ## 퀵 클로닝 ```bash cast voices clone sample.wav --name "My Clone" ``` 기본 출력은 클로닝된 voice ID이므로 스크립트에서 바로 사용할 수 있습니다: ```bash voice_id=$(cast voices clone sample.wav --name "Review Clone") cast "Short test line." --voice-id "$voice_id" --out review.wav cast voices delete "$voice_id" ``` ## JSON 출력으로 도구에 넘기기 ```bash cast voices clone sample.mp3 --name "Review Clone" --json ``` 에이전트나 다른 도구가 클로닝된 voice ID와 다음 단계 값을 구조화해서 받아야 하면 JSON 출력을 사용하세요. ## 프리미엄 클로닝 `--professional`과 샘플 보이스의 언어 코드를 추가하세요. 명령을 실행하면 학습이 비동기로 진행되는 동안 새 `uc_` 보이스 ID가 출력됩니다. ```bash voice_id=$(cast voices clone sample.wav --name "My Premium Voice" \ --professional --language kor) cast voices clone status "$voice_id" ``` 상태가 `completed` 또는 `failed`가 될 때까지 확인하세요. 한국어 샘플은 `kor`, 영어 샘플은 `eng`를 사용합니다. ## 클로닝 보이스로 음성 생성 ```bash cast "Hello from my cloned voice." \ --voice-id uc_xxx \ --emotion smart \ --out cloned.wav ``` ## 클로닝 보이스 정리 ```bash cast voices delete uc_xxx ``` ## 제약 | 항목 | 값 | |------|----| | 입력 오디오 | WAV 또는 MP3 | | 최대 파일 크기 | 25 MB | | 보이스 이름 | 1-30자 | | 모델 | `ssfm-v30` | | 프리미엄 클로닝 | `--professional`, `--language` 필수 | 클로닝 보이스는 프로젝트 자산처럼 관리하세요. 이름을 명확히 붙이고, 어떤 출력 파일에 사용했는지 기록하고, 임시 클론은 작업이 끝나면 삭제하세요. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 설정 ## 기본값 설정 매번 플래그를 전달하지 않도록 기본값을 저장할 수 있습니다: ```bash cast config set voice-id tc_xxx cast config set model ssfm-v21 cast config set volume 120 cast config list cast config unset volume ``` 사용 가능한 키: `voice-id`, `model`, `language`, `emotion`, `emotion-preset`, `emotion-intensity`, `volume`, `pitch`, `tempo`, `format`, `remove-silence-ms` ## 우선순위 설정은 다음 우선순위로 적용됩니다: ```text --flag > 환경 변수 > ~/.typecast/config.yaml > 기본값 ``` ## 환경 변수 `TYPECAST_` 접두사를 사용하여 모든 옵션을 환경 변수로 설정할 수 있습니다: | 변수 | 플래그 | |------|--------| | `TYPECAST_API_KEY` | `--api-key` | | `TYPECAST_VOICE_ID` | `--voice-id` | | `TYPECAST_MODEL` | `--model` | | `TYPECAST_LANGUAGE` | `--language` | | `TYPECAST_EMOTION` | `--emotion` | | `TYPECAST_EMOTION_PRESET` | `--emotion-preset` | | `TYPECAST_EMOTION_INTENSITY` | `--emotion-intensity` | | `TYPECAST_FORMAT` | `--format` | | `TYPECAST_VOLUME` | `--volume` | | `TYPECAST_PITCH` | `--pitch` | | `TYPECAST_TEMPO` | `--tempo` | | `TYPECAST_REMOVE_SILENCE_MS` | `--remove-silence-ms` | ## 무음 길이 조절 **Cast v1.0.10 이상**에서 `--remove-silence-ms`로 설정합니다. 기본값은 미설정이며 `0`이 아닙니다. ```bash cast "Hello. Thank you for listening." --voice-id tc_672c5f5ce59fac2a48faeaee --remove-silence-ms 300 ``` `remove_silence_ms`는 제거할 시간이 아니라 **남길 무음 길이**를 지정합니다. `0`부터 `1000`ms까지의 정수를 사용하세요. `0`은 검출된 무음을 제거하며, 생략하거나 `null`을 지정하면 지정 길이에 따른 무음 제거를 적용하지 않습니다. 일반·스트리밍·타임스탬프 TTS는 `output.remove_silence_ms`, Compose는 각 `tts` 세그먼트의 `segments[].output.remove_silence_ms`로 전달합니다. 반환 타임스탬프는 처리 후 오디오를 기준으로 하며, 명시적인 `pause` 세그먼트는 유지됩니다. 스트리밍의 기본 앞부분 무음 트림은 별개입니다. 특히 `0`처럼 작은 값에서는 재생 가능한 청크 수신에 간격이 생길 수 있으므로 충분한 재생 버퍼를 확보하고 실제 콘텐츠로 확인하세요. ```bash export TYPECAST_REMOVE_SILENCE_MS=300 cast config set remove-silence-ms 300 cast config unset remove-silence-ms ``` 환경 변수는 `TYPECAST_REMOVE_SILENCE_MS`, YAML 키는 `remove_silence_ms`입니다. 기존 우선순위(플래그 → 환경 변수 → 설정 파일 → 기본값)를 따릅니다. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 활용 예시 ```bash cast "$(cat script.txt)" ``` ```bash echo "System is ready." | cast cast "$(curl -s https://example.com/status.txt)" ``` ```bash cast "Chapter one." --out ch1.wav cast "Chapter two." --out ch2.wav cast "Chapter three." --out ch3.wav ``` ```bash cast "$(cat shorts-script.txt)" \ --out shorts-voiceover.wav \ --timestamp-out shorts-voiceover.srt ``` ```bash cast "$(cat preview-script.txt)" \ --out preview.wav \ --timestamp-out preview.vtt \ --timestamp-format vtt ``` ```bash voice_id=$(cast voices clone sample.wav --name "Draft Voice") cast "$(cat narration.txt)" --voice-id "$voice_id" --out draft.wav cast voices delete "$voice_id" ``` ```bash cast "It was a dark and stormy night." \ --emotion preset --emotion-preset normal --emotion-intensity 0.5 --out intro.wav cast "She opened the letter and gasped." \ --emotion preset --emotion-preset happy --emotion-intensity 1.5 --out climax.wav cast "He watched the train disappear into the fog." \ --emotion preset --emotion-preset sad --out farewell.wav ``` ```bash cast "I can't believe we actually made it!" --emotion smart \ --prev-text "We've been working on this for three years." \ --next-text "Let's celebrate tonight!" ``` ```bash cast "Hello, world!" --seed 42 --out hello.wav cast "Hello, world!" --seed 42 --out hello2.wav ``` ```bash cast "Bonjour le monde" --language fra cast "Hola mundo" --language spa ``` ```bash cast "Buy now, limited time offer!" --tempo 1.3 --pitch 2 cast "Relax and take a deep breath." --tempo 0.85 --volume 90 ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 문제 해결 - 설치가 정상적으로 완료되었는지 확인하세요. - Homebrew: `brew list neosapience/tap/cast`로 확인하세요. - Go: `$GOPATH/bin`이 `PATH`에 포함되어 있는지 확인하세요. - 새 터미널 세션을 열어보세요. - `cast login`으로 API 키를 다시 입력하세요. - [타입캐스트 API 콘솔](https://studio.typecast.ai/developers/api)에서 키가 유효한지 확인하세요. - `cast logout` 후 `cast login`으로 로컬 인증 정보를 초기화하세요. - 파일로 저장해보세요: `cast "test" --out test.wav`. - 시스템 오디오 출력 장치를 확인하세요. - 시스템 볼륨이 음소거 상태가 아닌지 확인하세요. ## 리소스 소스 코드 및 릴리스를 확인합니다. 타입캐스트 API를 탐색합니다. 사용 가능한 보이스를 둘러봅니다. API 키와 설정을 관리합니다. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 쇼츠 생성 쇼츠 제작은 빠른 반복이 중요합니다. 훅을 여러 개 만들고, 짧은 본문을 다듬고, 편집기에 바로 넣을 수 있는 음성 파일을 만들어야 합니다. CLI를 쓰면 에이전트가 스크립트 초안에서 음성 파일까지 한 흐름으로 만들 수 있습니다. ## 추천 흐름 자막과 편집을 고려해 문장을 짧게 유지합니다. ```text 25초 쇼츠 스크립트를 작성해줘. - 시작 훅 1개 - 짧은 본문 3문장 - 마지막 행동 유도 1문장 ``` ```bash cast "타입캐스트 음성을 가장 빠르게 테스트하는 방법입니다." \ --emotion smart \ --out shorts-scratch.wav ``` ```bash cast "Stop scrolling. Your app can speak in one command." \ --emotion preset --emotion-preset happy --emotion-intensity 1.3 \ --out hook-a.wav cast "Here is a terminal trick for instant AI voiceovers." \ --emotion smart \ --out hook-b.wav ``` ```bash cast "$(cat shorts-script.txt)" \ --voice-id tc_xxx \ --emotion smart \ --format mp3 \ --out shorts-final.mp3 ``` ```bash cast "$(cat shorts-script.txt)" \ --voice-id tc_xxx \ --emotion smart \ --out shorts-final.wav \ --timestamp-out shorts-final.srt ``` ## 에이전트 프롬프트 예시 ```text 쇼츠용 보이스오버를 만들어줘. 먼저 스크립트를 작성하고 다음 파일을 생성해줘: 1. hook-a.wav 2. hook-b.wav 3. final.mp3 4. final.srt CLI를 사용하고, 같은 최종 스크립트에서 자막을 생성하고, 승인된 테이크를 덮어쓰지 마. ``` ## 실무 팁 | 목표 | CLI 옵션 | |------|---------------| | 빠른 전달감 | `--tempo 1.08` ~ `--tempo 1.18` | | 에너지 있는 낭독 | `--emotion preset --emotion-preset happy --emotion-intensity 1.2` | | 자연스러운 문맥 | `--emotion smart --prev-text ... --next-text ...` | | 편집기용 파일 | `--out final.mp3 --format mp3` | | 자막 import | `--timestamp-out final.srt` | | 웹 preview 자막 | `--timestamp-out final.vtt --timestamp-format vtt` | | 캠페인 전용 보이스 | `cast voices clone sample.wav --name "Campaign Voice"` | | 재현 가능한 초안 | `--seed 42` | ## 자동화하면 좋은 부분 | 쇼츠 작업 | 추천 CLI 기능 | |-----------|--------------------| | 시작 훅 A/B 테스트 | `hook-a.wav`, `hook-b.wav`를 별도 파일로 생성 | | 편집용 자막 | `--timestamp-out final.srt` | | 브라우저 preview 자막 | `--timestamp-out final.vtt --timestamp-format vtt` | | 브랜드/크리에이터 보이스 | `cast voices clone` 후 `--voice-id uc_xxx` 사용 | | 회의 중 빠른 리뷰 | `--out` 없이 `cast "short sentence"`로 즉시 재생 | 훅은 별도 파일로 생성하세요. 전체 내레이션을 다시 만들지 않아도 시작 부분만 빠르게 비교할 수 있습니다. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 오디오북 생성 오디오북 생성에는 일관된 보이스, 반복 가능한 파일명, 챕터 단위 출력이 중요합니다. CLI를 쓰면 에이전트가 챕터 초안 생성, 특정 구간 재생성, 승인본 분리를 단순한 명령으로 처리할 수 있습니다. ## 기본값 준비 챕터를 생성하기 전에 보이스와 모델을 설정합니다: ```bash cast config set voice-id tc_xxx cast config set model ssfm-v30 cast config set format mp3 ``` 프로젝트 전용 보이스가 필요하면 먼저 클로닝하고 반환된 `uc_` 보이스 ID를 저장합니다: ```bash cast voices clone narrator-sample.wav --name "Narrator Draft" cast config set voice-id uc_xxx ``` ## 챕터 생성 ```bash cast "$(cat chapter-01.txt)" \ --emotion smart \ --out audiobook/chapter-01.mp3 \ --format mp3 cast "$(cat chapter-02.txt)" \ --emotion smart \ --out audiobook/chapter-02.mp3 \ --format mp3 ``` ## 자막 또는 리뷰용 타이밍 생성 ```bash cast "$(cat chapter-01.txt)" \ --emotion smart \ --out audiobook/chapter-01.wav \ --timestamp-out audiobook/chapter-01.timestamps.json ``` 상세 타이밍 검토에는 JSON을 사용하고, 오디오북 콘텐츠를 영상 preview로도 보여줘야 하면 `.srt` 또는 `.vtt`로 저장하세요. ## 장면별 감정 연출 톤이 명확한 짧은 구간은 preset emotion이 더 제어하기 쉽습니다: ```bash cast "The room fell silent as the letter slipped from her hand." \ --emotion preset \ --emotion-preset sad \ --emotion-intensity 1.2 \ --out audiobook/scene-letter.mp3 ``` 앞뒤 문맥이 중요한 구간은 smart emotion에 문맥을 함께 전달합니다: ```bash cast "She opened the door and froze." \ --emotion smart \ --prev-text "The hallway had been empty a moment ago." \ --next-text "A familiar voice whispered her name." \ --out audiobook/scene-door.mp3 ``` ## 에이전트 프롬프트 예시 ```text 챕터 텍스트 파일에서 오디오북 초안을 생성해줘. 모든 챕터에 같은 보이스를 사용해줘. 출력은 ./audiobook 아래에 저장해줘. 챕터마다 MP3 파일 1개를 생성해줘. 영상 preview가 필요한 챕터는 오디오 옆에 SRT 파일도 생성해줘. 실패한 챕터가 있으면 파일명을 보고하고 계속 진행해줘. ``` ## 파일명 규칙 | 자산 | 추천 파일명 | |------|-------------| | 전체 챕터 | `chapter-01.mp3` | | 장면 수정본 | `chapter-01-scene-03-v2.mp3` | | 승인된 최종본 | `chapter-01-final.mp3` | | 대체 전달 톤 | `chapter-01-alt-happy.mp3` | ## 고급 기능 사용 기준 | 필요 | 추천 기능 | |------|-----------| | 일관된 내레이터 정체성 | `cast config set voice-id ...` | | 샘플과 맞춘 임시 내레이터 | `cast voices clone narrator-sample.wav --name "Narrator Draft"` | | 챕터 리뷰용 타이밍 | `--timestamp-out chapter-01.timestamps.json` | | 챕터 영상 preview | `--timestamp-out chapter-01.srt` | | 빠른 승인 재생 | `--out` 없이 `cast "one review sentence"` | 승인된 오디오는 덮어쓰지 마세요. 수정본은 `-v2`, `-v3` 또는 톤 이름을 붙여 저장하도록 에이전트에게 지시하세요. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 에이전트 스킬 연결 CLI는 에이전트 스킬 안에서 쓰기 좋습니다. 타입캐스트 음성 생성을 셸 명령으로 노출하기 때문에, 에이전트가 텍스트를 작성하고 파일명을 정하고 `cast`를 호출해 재생 가능한 오디오 자산을 만들 수 있습니다. ## 스킬 지시문 템플릿 ```markdown # 타입캐스트 음성 생성 스킬 사용자가 보이스오버, 내레이션, 대사, 오디오 미리듣기를 요청하면 CLI를 사용합니다. Rules: - Never expose the API key. - Prefer `--out` so generated audio is saved as a file. - Use descriptive filenames. - Use `--emotion smart` for natural narration. - Use preset emotion when the user specifies a tone. - Use `--timestamp-out` when the user asks for captions, subtitles, timing, or lip-sync. - Use `cast voices clone` only when the user provides or approves a voice sample. - Report the output path after generation. ``` ## 최소 명령 세트 | 작업 | 명령어 | |------|--------| | 인증 확인 | `cast "test" --out typecast-test.wav` | | 내레이션 생성 | `cast "$(cat script.txt)" --emotion smart --out narration.wav` | | MP3 생성 | `cast "$(cat script.txt)" --format mp3 --out narration.mp3` | | 자막 생성 | `cast "$(cat script.txt)" --out narration.wav --timestamp-out narration.srt` | | 보이스 선택 | `cast voices pick` | | 보이스 클로닝 | `cast voices clone sample.wav --name "Project Voice"` | | 기본 보이스 저장 | `cast config set voice-id tc_xxx` | ## 에이전트 프롬프트 예시 ```text 타입캐스트 음성 생성 스킬을 사용해줘. script.txt에서 보이스오버 테이크 3개를 만들어줘: - neutral - energetic - soft ./voiceover 아래에 저장하고 파일명을 알려줘. ``` ## 기능 라우팅 에이전트가 요청에 맞는 가장 작은 CLI 기능을 고르게 하세요: | 사용자 요청 | 에이전트가 사용할 기능 | |-------------|------------------------| | "이 문장을 말해줘" | `--out` 없이 `cast "..."` | | "보이스오버 파일을 만들어줘" | `cast "$(cat script.txt)" --out narration.wav` | | "자막도 같이 만들어줘" | `--timestamp-out narration.srt` 추가 | | "이 샘플 목소리로 해줘" | `cast voices clone sample.wav --name ...` 후 `--voice-id uc_xxx` | | "몇 가지 목소리를 미리 들어보고 싶어" | `cast voices pick` 또는 `cast voices tournament` | ## 권장 안전장치 API 키는 `cast login` 또는 `TYPECAST_API_KEY`로 보관하세요. 공유 프롬프트나 생성 문서에 키를 붙여넣지 마세요. 긴 원고는 한 줄 명령에 직접 넣기보다 `cast "$(cat script.txt)"` 형태를 권장합니다. 리뷰가 끝난 오디오를 덮어쓰지 않도록, 수정본은 새 파일명으로 만들라고 에이전트에게 지시하세요. Claude Skills와 에이전트 워크플로우에서 타입캐스트를 사용하는 방법을 확인합니다. 기본 보이스, 모델, 출력 형식, 환경 변수를 설정합니다. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # PC 오디오 출력 에이전트가 로컬 컴퓨터를 제어하는 상황에서는 CLI로 짧은 텍스트를 음성으로 만들고 기본 오디오 출력 장치로 바로 재생할 수 있습니다. 작업 완료 알림, 손을 쓰지 않는 상태 안내, 빠른 음성 미리듣기에 유용합니다. 로컬 에이전트 피드백에는 `cast "message"`가 가장 단순한 실시간 느낌의 경로입니다. API 레벨의 chunked streaming이 필요하면 SDK로 타입캐스트 streaming endpoint를 사용하세요. ## 바로 말하기 ```bash cast "The export is complete." ``` CLI는 기본적으로 파일 저장 대신 생성된 오디오를 바로 재생합니다. ## 안정적인 재생을 위해 파일로 저장 긴 문장이나 반복 재생이 필요한 메시지는 먼저 파일로 저장합니다: ```bash cast "The build failed. Check the test report before pushing." --out agent-alert.wav ``` 이후 시스템 오디오 도구로 재생합니다: ```bash afplay agent-alert.wav ``` ```bash aplay agent-alert.wav ``` ## 에이전트 프롬프트 예시 ```text 긴 작업이 끝나면 PC 오디오로 짧은 상태 안내를 말해줘. CLI를 사용해줘. 말할 문장은 12단어 이하로 유지해줘. 재생에 실패하면 오디오 파일을 저장하고 경로를 알려줘. ``` ## 좋은 오디오 메시지 | 상황 | 추천 문장 | |------|-----------| | 작업 완료 | `The task is complete.` | | 사용자 입력 필요 | `I need your input to continue.` | | 테스트 실패 | `Tests failed. Please check the report.` | | 배포 준비 완료 | `The preview is ready.` | ## 오디오 경로 선택 | 필요 | 추천 경로 | |------|-----------| | 빠른 음성 상태 안내 | `cast "The preview is ready."` | | 같은 알림 반복 재생 | `--out agent-alert.wav`로 저장한 뒤 `afplay`로 재생 | | 에이전트 전용 보이스 | `cast config set voice-id tc_xxx` | | 프로젝트 전용 보이스 | `cast voices clone` 후 `--voice-id uc_xxx` 사용 | | 자세한 보고 | 한 문장만 말하고 세부 내용은 채팅이나 로그에 작성 | PC 오디오 메시지는 짧게 유지하세요. 자세한 상태는 한 문장으로 말하고, 세부 내용은 채팅이나 로그에 남기는 편이 좋습니다. --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Text To Speech > Generate speech from text using the specified voice model. Supports emotion, volume, pitch, and tempo customization. First, list all available voice models using the GET /v3/voices endpoint, then use the voice\_id from the response to generate speech with this endpoint. Each voice model has its own unique characteristics. See [Listing all voices](/docs/api-reference/voices/list-voices) for available voices. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "Production server" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/text-to-speech": { "post": { "tags": [ "Text-to-Speech" ], "x-mint": { "href": "/api-reference/text-to-speech/text-to-speech" }, "summary": "Text To Speech", "security": [ { "ApiKeyAuth": [] } ], "responses": { "200": { "content": { "audio/wav": { "schema": { "type": "string", "format": "binary", "description": "WAV audio file binary data. Uncompressed PCM audio with 16-bit depth, mono channel, 44100 Hz sample rate." }, "example": "[Binary audio data - WAV file content]" } }, "description": "Success - Returns audio file" }, "400": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid voice_id" } } }, "description": "Bad Request - Invalid parameters" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Unauthorized - Authentication failed" }, "402": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Insufficient credit" } } }, "description": "Payment Required - Insufficient credits" }, "404": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Voice not found" } } }, "description": "Not Found - Voice model not available" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" } } }, "description": "Validation Error - The request is invalid or the input text cannot be synthesized" }, "429": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Too many requests" } } }, "description": "Too Many Requests - Rate limit exceeded" }, "500": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "An unexpected error occurred" } } }, "description": "Internal Server Error - Server processing failed" } }, "deprecated": false, "description": "Generate speech from text using the specified voice model. Supports emotion, volume, pitch, and tempo customization.\r\n\r\nFirst, list all available voice models using the GET /v3/voices endpoint, then use the voice\\_id from the response to generate speech with this endpoint. Each voice model has its own unique characteristics. See [Listing all voices](/docs/api-reference/voices/list-voices) for available voices.", "operationId": "text_to_speech_v1_text_to_speech_post", "requestBody": { "content": { "application/json": { "schema": { "type": "object", "title": "TTSRequest", "required": [ "text", "model", "voice_id" ], "properties": { "seed": { "type": "integer", "anyOf": [ { "type": "integer", "maximum": 4294967295, "minimum": 0 }, { "type": "null" } ], "title": "Seed", "format": "uint32", "example": 42, "minimum": 0, "description": "Unsigned integer seed for reproducible speech generation. The same seed with the same input parameters will produce identical audio output.\r\n\r\n* Must be a non-negative integer (≥ 0). Negative values are not accepted.\r\n* If omitted, the server generates a random seed each time, producing slight variations." }, "text": { "type": "string", "title": "Text", "example": "Everything is so incredibly perfect that I feel like I'm dreaming.", "maxLength": 2000, "minLength": 1, "description": "Text to convert to speech. Minimum 1 character, maximum 2000 characters. Credits consumed based on text length. Supports multiple languages including English, Korean, Japanese, and Chinese. Special characters and punctuation are handled automatically." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "Voice model to use for speech synthesis.\r\n\r\n* **ssfm-v30**: Latest model with improved prosody and additional emotion presets (recommended)\r\n* **ssfm-v21**: Stable production model with reliable quality" }, "output": { "type": "object", "title": "", "properties": { "volume": { "anyOf": [ { "type": "integer", "maximum": 200, "minimum": 0 }, { "type": "null" } ], "title": "Volume", "example": 100, "description": "Adjusts the relative volume of the output audio: 0 (completely silent), 50 (half volume), 100 (standard volume, default), 150 (50% louder than standard), 200 (maximum volume, twice as loud as standard).\r\n\r\nSince this only scales the existing volume, using `volume` can amplify the loudness differences between voices if they have different baseline levels. For consistent output across all clips, use `target_lufs` instead.\r\n\r\n- **Note:** This parameter cannot be used simultaneously with the `target_lufs` parameter.\r\n\r\nRequired range: 0 <= x <= 200\r\n" }, "audio_pitch": { "type": "integer", "title": "Audio Pitch", "default": 0, "example": 0, "maximum": 12, "minimum": -12, "description": "Adjusts the pitch in semitones to affect perceived gender and age: -12 (one octave lower, deeper voice), -6 (half octave lower), 0 (original pitch, default), +6 (half octave higher), +12 (one octave higher, higher voice)" }, "audio_tempo": { "type": "number", "title": "Audio Tempo", "default": 1, "example": 1, "maximum": 2, "minimum": 0.5, "description": "Controls speech speed: 0.5 (half speed, very slow and clear), 0.75 (slightly slower than normal), 1.0 (normal speaking speed, default), 1.5 (50% faster than normal), 2.0 (double speed, very fast speech)" }, "target_lufs": { "type": "number", "anyOf": [ { "type": "number", "maximum": 0, "minimum": -70 }, { "type": "null" } ], "title": "Target Lufs", "example": -14, "description": "Sets the target absolute loudness (LUFS) for the output audio. This normalizes all generated voices to a consistent volume level, regardless of the original source's loudness. Values closer to 0 are louder, while values closer to -70 are quieter.\r\n\r\n- Required range: -70 <= x <= 0\r\n- Recommended values: -14 (common streaming standard), -23 (broadcast standard)\r\n- **Note:** This parameter cannot be used simultaneously with the `volume` parameter. Use `target_lufs` for consistent absolute loudness across different clips, or use `volume` for traditional relative scaling.\r\n" }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "default": "wav", "example": "wav", "description": "Output audio format.\r\n\r\n**WAV format:**\r\n- Uncompressed PCM audio\r\n- 16-bit depth, mono channel, 44100 Hz sample rate\r\n- Higher quality, larger file size\r\n- Recommended for professional audio production\r\n\r\n**MP3 format:**\r\n- Compressed MPEG Layer III audio\r\n- 320 kbps bitrate, 44100 Hz sample rate\r\n- Smaller file size\r\n- Recommended for web streaming and distribution\r\n" }, "remove_silence_ms": { "anyOf": [ { "type": "integer", "maximum": 1000, "minimum": 0 }, { "type": "null" } ], "title": "Remove Silence Ms", "default": null, "example": 100, "description": "When enabled, shortens detected silences longer than the specified duration to that duration. The value is in milliseconds (ms). The value is the duration of silence to retain, not the amount to remove.\r\n\r\n**Accepted values:**\r\n\r\n- An integer from **0 to 1000**, with a recommended range of 0 to 200.\r\n- Omitted or `null`: silence removal is disabled.\r\n- `0`: removes detected silence longer than 0 ms. **This does not disable the feature.**\r\n- Booleans, strings, fractional values, and out-of-range values are invalid.\r\n\r\n**Example:** `100` shortens detected silences longer than 100 ms to 100 ms. Lower values include shorter silence segments for removal and leave less silence in each affected segment." } }, "description": "Audio output settings including volume (0-200), pitch (-12 to +12 semitones), tempo (0.5x to 2.0x), and format (wav/mp3) for controlling the final audio characteristics" }, "prompt": { "oneOf": [ { "$ref": "#/components/schemas/SmartPrompt" }, { "$ref": "#/components/schemas/PresetPrompt" }, { "$ref": "#/components/schemas/Prompt" } ], "title": "Prompt", "description": "Emotion and style settings for the generated speech, including emotion type (happy/sad/angry/normal) and intensity (0.0 to 2.0) to control the emotional expression" }, "language": { "type": "string", "title": "Language", "example": "eng", "description": "Language code following ISO 639-3 standard. Case-insensitive (both \"ENG\" and \"eng\" are accepted). If not provided, will be auto-detected based on text content.\r\n\r\n
\r\n ssfm-v30 Supported Languages (37)\r\n\r\n | Code | Language | Code | Language | Code | Language |\r\n | ---- | --------- | ---- | ---------- | ---- | ---------- |\r\n | ARA | Arabic | IND | Indonesian | POR | Portuguese |\r\n | BEN | Bengali | ITA | Italian | RON | Romanian |\r\n | BUL | Bulgarian | JPN | Japanese | RUS | Russian |\r\n | CES | Czech | KOR | Korean | SLK | Slovak |\r\n | DAN | Danish | MSA | Malay | SPA | Spanish |\r\n | DEU | German | NAN | Min Nan | SWE | Swedish |\r\n | ELL | Greek | NLD | Dutch | TAM | Tamil |\r\n | ENG | English | NOR | Norwegian | TGL | Tagalog |\r\n | FIN | Finnish | PAN | Punjabi | THA | Thai |\r\n | FRA | French | POL | Polish | TUR | Turkish |\r\n | HIN | Hindi | UKR | Ukrainian | VIE | Vietnamese |\r\n | HRV | Croatian | YUE | Cantonese | ZHO | Chinese |\r\n | HUN | Hungarian | | | | |\r\n
\r\n\r\n
\r\n ssfm-v21 Supported Languages (27)\r\n\r\n | Code | Language | Code | Language | Code | Language |\r\n | ---- | --------- | ---- | ---------- | ---- | --------- |\r\n | ARA | Arabic | IND | Indonesian | RON | Romanian |\r\n | BUL | Bulgarian | ITA | Italian | RUS | Russian |\r\n | CES | Czech | JPN | Japanese | SLK | Slovak |\r\n | DAN | Danish | KOR | Korean | SPA | Spanish |\r\n | DEU | German | MSA | Malay | SWE | Swedish |\r\n | ELL | Greek | NLD | Dutch | TAM | Tamil |\r\n | ENG | English | POL | Polish | TGL | Tagalog |\r\n | FIN | Finnish | POR | Portuguese | UKR | Ukrainian |\r\n | FRA | French | HRV | Croatian | ZHO | Chinese |\r\n
" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "Voice identifier. Two prefixes are supported:\r\n\r\n* `tc_` — Built-in Typecast voices (e.g., `tc_60e5426de8b95f1d3000d7b5`). See [Listing all voices](/docs/api-reference/voices/list-voices) for available IDs.\r\n* `uc_` — Custom voices created via [Instant cloning](/docs/api-reference/voices/instant-cloning) (e.g., `uc_64a1b2c3d4e5f6a7b8c9d0e1`). Only the owner of a cloned voice can use it.\r\n\r\nCase-sensitive: must use lowercase prefix." } }, "description": "Text-to-speech request parameters" } } }, "required": true, "description": "" }, "x-codeSamples": [ { "lang": "cURL", "label": "cURL (save to file)", "source": "curl --request POST \\\r\n --url https://api.typecast.ai/v1/text-to-speech \\\r\n --header 'Content-Type: application/json' \\\r\n --header 'X-API-KEY: ' \\\r\n --output output.wav \\\r\n --data @- <\",\r\n \"Content-Type\": \"application/json\",\r\n}\r\npayload = {\r\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\r\n \"text\": \"Everything is so incredibly perfect that I feel like I'm dreaming.\",\r\n \"model\": \"ssfm-v30\",\r\n \"language\": \"eng\",\r\n \"prompt\": {\r\n \"emotion_type\": \"smart\",\r\n \"previous_text\": \"I feel like I'm walking on air and I just want to scream with joy!\",\r\n \"next_text\": \"I am literally bursting with happiness and I never want this feeling to end!\",\r\n },\r\n \"output\": {\r\n \"volume\": 100,\r\n \"audio_pitch\": 0,\r\n \"audio_tempo\": 1,\r\n \"audio_format\": \"wav\",\r\n },\r\n \"seed\": 42,\r\n}\r\n\r\nresponse = requests.post(f\"{API_HOST}/v1/text-to-speech\", headers=headers, json=payload, timeout=60)\r\nresponse.raise_for_status()\r\n\r\nwith open(\"output.wav\", \"wb\") as f:\r\n f.write(response.content)\r\nprint(f\"Saved {len(response.content)} bytes to output.wav\")\r\n" }, { "lang": "C#", "label": "C# (HttpClient)", "source": "using System;\r\nusing System.Net.Http;\r\nusing System.Text;\r\nusing System.Threading.Tasks;\r\n\r\nvar client = new HttpClient();\r\nclient.DefaultRequestHeaders.Add(\"X-API-KEY\", \"\");\r\n\r\nvar requestBody = @\"{\r\n \"\"voice_id\"\": \"\"tc_60e5426de8b95f1d3000d7b5\"\",\r\n \"\"text\"\": \"\"Everything is so incredibly perfect that I feel like I'm dreaming.\"\",\r\n \"\"model\"\": \"\"ssfm-v30\"\",\r\n \"\"language\"\": \"\"eng\"\",\r\n \"\"prompt\"\": {\r\n \"\"emotion_type\"\": \"\"smart\"\",\r\n \"\"previous_text\"\": \"\"I feel like I'm walking on air and I just want to scream with joy!\"\",\r\n \"\"next_text\"\": \"\"I am literally bursting with happiness and I never want this feeling to end!\"\"\r\n },\r\n \"\"output\"\": {\r\n \"\"volume\"\": 100,\r\n \"\"audio_pitch\"\": 0,\r\n \"\"audio_tempo\"\": 1,\r\n \"\"audio_format\"\": \"\"wav\"\"\r\n },\r\n \"\"seed\"\": 42\r\n}\";\r\n\r\nvar content = new StringContent(requestBody, Encoding.UTF8, \"application/json\");\r\nvar response = await client.PostAsync(\"https://api.typecast.ai/v1/text-to-speech\", content);\r\n\r\nif (response.IsSuccessStatusCode)\r\n{\r\n var audioBytes = await response.Content.ReadAsByteArrayAsync();\r\n await File.WriteAllBytesAsync(\"output.wav\", audioBytes);\r\n Console.WriteLine(\"Audio saved to output.wav\");\r\n}\r\n" }, { "lang": "Kotlin", "label": "Kotlin (OkHttp)", "source": "import okhttp3.MediaType.Companion.toMediaType\r\nimport okhttp3.OkHttpClient\r\nimport okhttp3.Request\r\nimport okhttp3.RequestBody.Companion.toRequestBody\r\nimport java.io.File\r\n\r\nval client = OkHttpClient()\r\nval mediaType = \"application/json\".toMediaType()\r\n\r\nval requestBody = \"\"\"\r\n{\r\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\r\n \"text\": \"Everything is so incredibly perfect that I feel like I'm dreaming.\",\r\n \"model\": \"ssfm-v30\",\r\n \"language\": \"eng\",\r\n \"prompt\": {\r\n \"emotion_type\": \"smart\",\r\n \"previous_text\": \"I feel like I'm walking on air and I just want to scream with joy!\",\r\n \"next_text\": \"I am literally bursting with happiness and I never want this feeling to end!\"\r\n },\r\n \"output\": {\r\n \"volume\": 100,\r\n \"audio_pitch\": 0,\r\n \"audio_tempo\": 1,\r\n \"audio_format\": \"wav\"\r\n },\r\n \"seed\": 42\r\n}\r\n\"\"\".trimIndent()\r\n\r\nval request = Request.Builder()\r\n .url(\"https://api.typecast.ai/v1/text-to-speech\")\r\n .addHeader(\"X-API-KEY\", \"\")\r\n .addHeader(\"Content-Type\", \"application/json\")\r\n .post(requestBody.toRequestBody(mediaType))\r\n .build()\r\n\r\nclient.newCall(request).execute().use { response ->\r\n if (response.isSuccessful) {\r\n response.body?.bytes()?.let {\r\n File(\"output.wav\").writeBytes(it)\r\n println(\"Audio saved to output.wav\")\r\n }\r\n }\r\n}\r\n" }, { "lang": "C++", "label": "C++ (libcurl)", "source": "#include \r\n#include \r\n#include \r\n\r\nsize_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {\r\n ((std::string*)userp)->append((char*)contents, size * nmemb);\r\n return size * nmemb;\r\n}\r\n\r\nint main() {\r\n CURL* curl = curl_easy_init();\r\n if(curl) {\r\n std::string readBuffer;\r\n struct curl_slist* headers = NULL;\r\n\r\n headers = curl_slist_append(headers, \"Content-Type: application/json\");\r\n headers = curl_slist_append(headers, \"X-API-KEY: \");\r\n\r\n std::string jsonData = R\"({\r\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\r\n \"text\": \"Everything is so incredibly perfect that I feel like I'm dreaming.\",\r\n \"model\": \"ssfm-v30\",\r\n \"language\": \"eng\",\r\n \"prompt\": {\r\n \"emotion_type\": \"smart\",\r\n \"previous_text\": \"I feel like I'm walking on air and I just want to scream with joy!\",\r\n \"next_text\": \"I am literally bursting with happiness and I never want this feeling to end!\"\r\n },\r\n \"output\": {\r\n \"volume\": 100,\r\n \"audio_pitch\": 0,\r\n \"audio_tempo\": 1,\r\n \"audio_format\": \"wav\"\r\n },\r\n \"seed\": 42\r\n })\";\r\n\r\n curl_easy_setopt(curl, CURLOPT_URL, \"https://api.typecast.ai/v1/text-to-speech\");\r\n curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);\r\n curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonData.c_str());\r\n curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);\r\n curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);\r\n\r\n CURLcode res = curl_easy_perform(curl);\r\n if(res == CURLE_OK) {\r\n std::ofstream outFile(\"output.wav\", std::ios::binary);\r\n outFile.write(readBuffer.c_str(), readBuffer.size());\r\n outFile.close();\r\n }\r\n\r\n curl_slist_free_all(headers);\r\n curl_easy_cleanup(curl);\r\n }\r\n return 0;\r\n}\r\n" }, { "lang": "C", "label": "C (libcurl)", "source": "#include \r\n#include \r\n#include \r\n#include \r\n\r\ntypedef struct {\r\n char* data;\r\n size_t size;\r\n} MemoryStruct;\r\n\r\nsize_t WriteMemoryCallback(void* contents, size_t size, size_t nmemb, void* userp) {\r\n size_t realsize = size * nmemb;\r\n MemoryStruct* mem = (MemoryStruct*)userp;\r\n\r\n char* ptr = realloc(mem->data, mem->size + realsize + 1);\r\n if(!ptr) return 0;\r\n\r\n mem->data = ptr;\r\n memcpy(&(mem->data[mem->size]), contents, realsize);\r\n mem->size += realsize;\r\n mem->data[mem->size] = 0;\r\n\r\n return realsize;\r\n}\r\n\r\nint main(void) {\r\n CURL* curl;\r\n CURLcode res;\r\n MemoryStruct chunk = {NULL, 0};\r\n\r\n curl_global_init(CURL_GLOBAL_ALL);\r\n curl = curl_easy_init();\r\n\r\n if(curl) {\r\n struct curl_slist* headers = NULL;\r\n headers = curl_slist_append(headers, \"Content-Type: application/json\");\r\n headers = curl_slist_append(headers, \"X-API-KEY: \");\r\n\r\n const char* jsonData = \"{\"\r\n \"\\\"voice_id\\\":\\\"tc_60e5426de8b95f1d3000d7b5\\\",\"\r\n \"\\\"text\\\":\\\"Everything is so incredibly perfect that I feel like I'm dreaming.\\\",\"\r\n \"\\\"model\\\":\\\"ssfm-v30\\\",\"\r\n \"\\\"language\\\":\\\"eng\\\",\"\r\n \"\\\"output\\\":{\\\"volume\\\":100,\\\"audio_pitch\\\":0,\\\"audio_tempo\\\":1,\\\"audio_format\\\":\\\"wav\\\"},\"\r\n \"\\\"seed\\\":42\"\r\n \"}\";\r\n\r\n curl_easy_setopt(curl, CURLOPT_URL, \"https://api.typecast.ai/v1/text-to-speech\");\r\n curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);\r\n curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonData);\r\n curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);\r\n curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&chunk);\r\n\r\n res = curl_easy_perform(curl);\r\n\r\n if(res == CURLE_OK) {\r\n FILE* fp = fopen(\"output.wav\", \"wb\");\r\n fwrite(chunk.data, 1, chunk.size, fp);\r\n fclose(fp);\r\n }\r\n\r\n curl_slist_free_all(headers);\r\n curl_easy_cleanup(curl);\r\n free(chunk.data);\r\n }\r\n\r\n curl_global_cleanup();\r\n return 0;\r\n}\r\n" }, { "lang": "Swift", "label": "Swift (URLSession)", "source": "import Foundation\r\n\r\nlet url = URL(string: \"https://api.typecast.ai/v1/text-to-speech\")!\r\nvar request = URLRequest(url: url)\r\nrequest.httpMethod = \"POST\"\r\nrequest.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\r\nrequest.setValue(\"\", forHTTPHeaderField: \"X-API-KEY\")\r\n\r\nlet requestBody: [String: Any] = [\r\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\r\n \"text\": \"Everything is so incredibly perfect that I feel like I'm dreaming.\",\r\n \"model\": \"ssfm-v30\",\r\n \"language\": \"eng\",\r\n \"prompt\": [\r\n \"emotion_type\": \"smart\",\r\n \"previous_text\": \"I feel like I'm walking on air and I just want to scream with joy!\",\r\n \"next_text\": \"I am literally bursting with happiness and I never want this feeling to end!\"\r\n ],\r\n \"output\": [\r\n \"volume\": 100,\r\n \"audio_pitch\": 0,\r\n \"audio_tempo\": 1,\r\n \"audio_format\": \"wav\"\r\n ],\r\n \"seed\": 42\r\n]\r\n\r\nrequest.httpBody = try? JSONSerialization.data(withJSONObject: requestBody)\r\n\r\nlet task = URLSession.shared.dataTask(with: request) { data, response, error in\r\n if let data = data {\r\n try? data.write(to: URL(fileURLWithPath: \"output.wav\"))\r\n print(\"Audio saved to output.wav\")\r\n }\r\n}\r\ntask.resume()\r\n" }, { "lang": "Rust", "label": "Rust (reqwest)", "source": "use reqwest;\r\nuse serde_json::json;\r\nuse std::fs::File;\r\nuse std::io::Write;\r\n\r\n#[tokio::main]\r\nasync fn main() -> Result<(), Box> {\r\n let client = reqwest::Client::new();\r\n\r\n let request_body = json!({\r\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\r\n \"text\": \"Everything is so incredibly perfect that I feel like I'm dreaming.\",\r\n \"model\": \"ssfm-v30\",\r\n \"language\": \"eng\",\r\n \"prompt\": {\r\n \"emotion_type\": \"smart\",\r\n \"previous_text\": \"I feel like I'm walking on air and I just want to scream with joy!\",\r\n \"next_text\": \"I am literally bursting with happiness and I never want this feeling to end!\"\r\n },\r\n \"output\": {\r\n \"volume\": 100,\r\n \"audio_pitch\": 0,\r\n \"audio_tempo\": 1,\r\n \"audio_format\": \"wav\"\r\n },\r\n \"seed\": 42\r\n });\r\n\r\n let response = client\r\n .post(\"https://api.typecast.ai/v1/text-to-speech\")\r\n .header(\"X-API-KEY\", \"\")\r\n .header(\"Content-Type\", \"application/json\")\r\n .json(&request_body)\r\n .send()\r\n .await?;\r\n\r\n if response.status().is_success() {\r\n let bytes = response.bytes().await?;\r\n let mut file = File::create(\"output.wav\")?;\r\n file.write_all(&bytes)?;\r\n println!(\"Audio saved to output.wav\");\r\n }\r\n\r\n Ok(())\r\n}\r\n" }, { "lang": "JavaScript", "label": "JavaScript (Node.js)", "source": "// Node 18+ (built-in fetch).\r\nimport { writeFile } from \"node:fs/promises\";\r\n\r\nconst response = await fetch(\"https://api.typecast.ai/v1/text-to-speech\", {\r\n method: \"POST\",\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n \"X-API-KEY\": \"\",\r\n },\r\n body: JSON.stringify({\r\n voice_id: \"tc_60e5426de8b95f1d3000d7b5\",\r\n text: \"Everything is so incredibly perfect that I feel like I'm dreaming.\",\r\n model: \"ssfm-v30\",\r\n language: \"eng\",\r\n prompt: {\r\n emotion_type: \"smart\",\r\n previous_text: \"I feel like I'm walking on air and I just want to scream with joy!\",\r\n next_text: \"I am literally bursting with happiness and I never want this feeling to end!\",\r\n },\r\n output: { volume: 100, audio_pitch: 0, audio_tempo: 1, audio_format: \"wav\" },\r\n seed: 42,\r\n }),\r\n});\r\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\r\n\r\nconst buffer = Buffer.from(await response.arrayBuffer());\r\nawait writeFile(\"output.wav\", buffer);\r\nconsole.log(`Saved ${buffer.length} bytes to output.wav`);\r\n" }, { "lang": "PHP", "label": "PHP (curl)", "source": " \"tc_60e5426de8b95f1d3000d7b5\",\r\n \"text\" => \"Everything is so incredibly perfect that I feel like I'm dreaming.\",\r\n \"model\" => \"ssfm-v30\",\r\n \"language\" => \"eng\",\r\n \"prompt\" => [\r\n \"emotion_type\" => \"smart\",\r\n \"previous_text\" => \"I feel like I'm walking on air and I just want to scream with joy!\",\r\n \"next_text\" => \"I am literally bursting with happiness and I never want this feeling to end!\",\r\n ],\r\n \"output\" => [\"volume\" => 100, \"audio_pitch\" => 0, \"audio_tempo\" => 1, \"audio_format\" => \"wav\"],\r\n \"seed\" => 42,\r\n]);\r\n\r\n$ch = curl_init(\"https://api.typecast.ai/v1/text-to-speech\");\r\ncurl_setopt_array($ch, [\r\n CURLOPT_POST => true,\r\n CURLOPT_RETURNTRANSFER => true,\r\n CURLOPT_HTTPHEADER => [\r\n \"Content-Type: application/json\",\r\n \"X-API-KEY: \",\r\n ],\r\n CURLOPT_POSTFIELDS => $payload,\r\n]);\r\n$audio = curl_exec($ch);\r\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\r\nif ($status !== 200) {\r\n fwrite(STDERR, \"HTTP $status\\n\");\r\n exit(1);\r\n}\r\nfile_put_contents(\"output.wav\", $audio);\r\necho \"Saved \" . strlen($audio) . \" bytes to output.wav\\n\";\r\n" }, { "lang": "Go", "label": "Go (net/http)", "source": "package main\r\n\r\nimport (\r\n \"bytes\"\r\n \"fmt\"\r\n \"io\"\r\n \"net/http\"\r\n \"os\"\r\n)\r\n\r\nfunc main() {\r\n body := []byte(`{\r\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\r\n \"text\": \"Everything is so incredibly perfect that I feel like I'm dreaming.\",\r\n \"model\": \"ssfm-v30\",\r\n \"language\": \"eng\",\r\n \"prompt\": {\r\n \"emotion_type\": \"smart\",\r\n \"previous_text\": \"I feel like I'm walking on air and I just want to scream with joy!\",\r\n \"next_text\": \"I am literally bursting with happiness and I never want this feeling to end!\"\r\n },\r\n \"output\": {\"volume\": 100, \"audio_pitch\": 0, \"audio_tempo\": 1, \"audio_format\": \"wav\"},\r\n \"seed\": 42\r\n }`)\r\n\r\n req, _ := http.NewRequest(\"POST\", \"https://api.typecast.ai/v1/text-to-speech\", bytes.NewReader(body))\r\n req.Header.Set(\"Content-Type\", \"application/json\")\r\n req.Header.Set(\"X-API-KEY\", \"\")\r\n\r\n resp, err := http.DefaultClient.Do(req)\r\n if err != nil {\r\n panic(err)\r\n }\r\n defer resp.Body.Close()\r\n\r\n out, _ := os.Create(\"output.wav\")\r\n defer out.Close()\r\n n, _ := io.Copy(out, resp.Body)\r\n fmt.Printf(\"Saved %d bytes to output.wav\\n\", n)\r\n}\r\n" }, { "lang": "Java", "label": "Java (HttpClient)", "source": "// Java 11+ HttpClient with file body handler.\r\nimport java.net.URI;\r\nimport java.net.http.HttpClient;\r\nimport java.net.http.HttpRequest;\r\nimport java.net.http.HttpResponse;\r\nimport java.nio.file.Path;\r\n\r\npublic class TextToSpeech {\r\n public static void main(String[] args) throws Exception {\r\n String body = \"\"\"\r\n {\r\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\r\n \"text\": \"Everything is so incredibly perfect that I feel like I'm dreaming.\",\r\n \"model\": \"ssfm-v30\",\r\n \"language\": \"eng\",\r\n \"prompt\": {\r\n \"emotion_type\": \"smart\",\r\n \"previous_text\": \"I feel like I'm walking on air and I just want to scream with joy!\",\r\n \"next_text\": \"I am literally bursting with happiness and I never want this feeling to end!\"\r\n },\r\n \"output\": {\"volume\": 100, \"audio_pitch\": 0, \"audio_tempo\": 1, \"audio_format\": \"wav\"},\r\n \"seed\": 42\r\n }\r\n \"\"\";\r\n\r\n HttpRequest request = HttpRequest.newBuilder()\r\n .uri(URI.create(\"https://api.typecast.ai/v1/text-to-speech\"))\r\n .header(\"Content-Type\", \"application/json\")\r\n .header(\"X-API-KEY\", \"\")\r\n .POST(HttpRequest.BodyPublishers.ofString(body))\r\n .build();\r\n\r\n HttpResponse response = HttpClient.newHttpClient()\r\n .send(request, HttpResponse.BodyHandlers.ofFile(Path.of(\"output.wav\")));\r\n\r\n System.out.println(\"Audio saved to \" + response.body());\r\n }\r\n}\r\n" }, { "lang": "Ruby", "label": "Ruby (net/http)", "source": "require \"net/http\"\r\nrequire \"uri\"\r\nrequire \"json\"\r\n\r\nuri = URI(\"https://api.typecast.ai/v1/text-to-speech\")\r\nhttp = Net::HTTP.new(uri.host, uri.port)\r\nhttp.use_ssl = true\r\n\r\nreq = Net::HTTP::Post.new(uri)\r\nreq[\"Content-Type\"] = \"application/json\"\r\nreq[\"X-API-KEY\"] = \"\"\r\nreq.body = {\r\n voice_id: \"tc_60e5426de8b95f1d3000d7b5\",\r\n text: \"Everything is so incredibly perfect that I feel like I'm dreaming.\",\r\n model: \"ssfm-v30\",\r\n language: \"eng\",\r\n prompt: {\r\n emotion_type: \"smart\",\r\n previous_text: \"I feel like I'm walking on air and I just want to scream with joy!\",\r\n next_text: \"I am literally bursting with happiness and I never want this feeling to end!\",\r\n },\r\n output: { volume: 100, audio_pitch: 0, audio_tempo: 1, audio_format: \"wav\" },\r\n seed: 42,\r\n}.to_json\r\n\r\nresp = http.request(req)\r\nraise \"HTTP #{resp.code}\" unless resp.code == \"200\"\r\n\r\nFile.binwrite(\"output.wav\", resp.body)\r\nputs \"Saved #{resp.body.bytesize} bytes to output.wav\"\r\n" }, { "lang": "cURL", "label": "Silence removal", "source": "curl --request POST 'https://api.typecast.ai/v1/text-to-speech' \\\r\n --header 'X-API-KEY: ' \\\r\n --header 'Content-Type: application/json' \\\r\n --output review.wav \\\r\n --data-binary @- <<'JSON'\r\n{\r\n \"voice_id\": \"\",\r\n \"text\": \"Hello. Thank you for listening.\",\r\n \"model\": \"ssfm-v30\",\r\n \"output\": {\r\n \"audio_format\": \"wav\",\r\n \"remove_silence_ms\": 300\r\n }\r\n}\r\nJSON\r\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "Maximum number of concurrent requests allowed" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "Maximum number of custom voices (created via instant cloning) allowed on the current plan." }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "Usage limit information" }, "Prompt": { "title": "Prompt (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "Emotion preset to apply.\r\n\r\nSupported emotions for ssfm-v21: normal, happy, sad, angry\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "example": 1, "description": "Controls the strength of emotional expression (0.0 to 2.0).\r\n\r\n- 0.0: Completely neutral\r\n- 1.0: Standard expression (default)\r\n- 2.0: Maximum intensity\r\n" } }, "description": "Emotion and style settings for the generated speech." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "Age group classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **child**: Child voice (under 12 years old)\n- **teenager**: Teenage voice (13-19 years old)\n- **young_adult**: Young adult voice (20-35 years old)\n- **middle_age**: Middle-aged voice (36-60 years old)\n- **elder**: Elder voice (over 60 years old)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "Total monthly credits provided by the plan" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "Number of credits used" } }, "description": "Credit usage information" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group classification (child/teenager/young_adult/middle_age/elder)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender classification (male/female)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "List of supported TTS models with their available emotions (e.g., [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix (e.g., `tc_60e5426de8b95f1d3000d7b5`); cloned custom voices created via `POST /v1/voices/clone` use the `uc_` prefix and are also returned by `/v2/voices` for the owner." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "List of use case categories this voice is suitable for" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "Human-readable name of the voice" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type — `original` for Typecast-provided stock voices, `custom` for user-cloned voices." } }, "description": "V2 Voice response model with model-grouped emotions and enhanced metadata" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender (`male` or `female`)." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "Supported TTS models and their available emotions." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix and custom voices use the `uc_` prefix." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "Recommended use-case categories." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "Localized voice names, such as `{\"eng\": \"Daejin\", \"kor\": \"대진\"}`." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type (`original` or `custom`)." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "Preview audio URL when available." } }, "description": "Voice metadata with localized names and a preview audio URL." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "Subscription plan for the API.\n\nAvailable values:\n- **free**: Free tier with limited credits\n- **lite**: Lite plan with moderate credits\n- **plus**: Plus plan with higher credits\n- **custom**: Custom enterprise plan" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "TTS model version to use for speech synthesis. Different models offer varying capabilities and quality levels.\n\nAvailable models:\n- **ssfm-v30**: Latest model with improved prosody and additional emotion presets (recommended)\n- **ssfm-v21**: Stable production model with proven reliability and consistent quality\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS model version (e.g., ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "List of supported emotions for this model" } }, "description": "Model information including version and supported emotions" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "English voice name." }, "kor": { "type": "string", "title": "Kor", "description": "Korean voice name." } }, "description": "Localized voice names keyed by ISO 639-3 language code." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "Voice type classification.\n\n- `original` — Typecast-provided stock voices available to every account.\n- `custom` — Voices the user created by uploading or cloning their own sample." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "Gender classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **male**: Male voice\n- **female**: Female voice\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "Available emotion presets for speech synthesis. Each emotion affects the tone, pace, and expressiveness of the generated speech.\n\n**ssfm-v21 Supported Emotions (4 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n\n**ssfm-v30 Supported Emotions (7 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n- whisper: Soft, quiet speech\n- toneup: Higher tonal emphasis\n- tonedown: Lower tonal emphasis\n\nCheck available emotions for each voice through the /v2/voices API response.\n" }, "SmartPrompt": { "type": "object", "title": "SmartPrompt (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "Text that comes AFTER the `text` field in TTSRequest. Provides forward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model anticipate emotional transitions\r\n- Leave empty if no following context is available\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "Discriminator field to identify the prompt type. Must be set to \"smart\" for context-aware emotion inference.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "Text that comes BEFORE the `text` field in TTSRequest. Provides backward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model understand emotional build-up and context\r\n- Leave empty if no preceding context is available\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "PresetPrompt (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "Discriminator field to identify the prompt type. Must be set to \"preset\" for preset-based emotion control.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "Emotion preset to apply to the generated speech.\r\n\r\nSupported emotions: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "Controls the strength of emotional expression in the generated speech.\r\n\r\n- 0.0: Completely neutral, no emotional coloring\r\n- 0.5: Subtle emotional hints\r\n- 1.0: Standard emotional expression (default)\r\n- 1.5: Strong emotional emphasis\r\n- 2.0: Maximum intensity, highly expressive\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "Voice use case categories for content type filtering. Each voice is tagged with one or more use cases indicating its suitability for specific content types.\n\n**Available Use Cases:**\n- **Announcer**: Public announcements and presentations\n- **Anime**: Animation and character voices\n- **Audiobook**: Long-form narration and storytelling\n- **Conversational**: Chatbots and conversational AI\n- **Documentary**: Documentary narration and commentary\n- **E-learning**: Educational content and tutorials\n- **Rapper**: Rap and music performance\n- **Game**: Video game characters and narration\n- **Tiktok/Reels**: Short-form social media content\n- **News**: News broadcasting\n- **Podcast**: Broadcasting and podcast production\n- **Voicemail**: IVR systems and voice assistants\n- **Ads**: Advertising and promotional content\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "Error message describing the issue" }, "message": { "type": "string", "description": "Human-readable message for structured errors" }, "error_code": { "type": "string", "description": "Machine-readable error code for structured errors" } }, "description": "API errors use `detail` for legacy and validation errors, or `error_code` and `message` for structured errors." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "Failure reason when `status` is `failed`." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "source": { "type": "string", "title": "Source", "description": "Creation method (`instant` or `professional`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "Creation time in UTC." } }, "description": "A custom voice owned by the authenticated account." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "Recommendation relevance score. Higher values indicate a stronger match for the query." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "Typecast voice identifier with the `tc_` prefix. Use this value as `voice_id` in text-to-speech requests." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "Human-readable voice name." } }, "description": "Recommended voice candidate returned by `GET /v1/voices/recommendations`, sorted by relevance." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "Current creation or training status of a custom voice." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Human-readable voice name (1-30 characters)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "Engine model the voice was cloned for (`ssfm-v21` or `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "Custom voice identifier with the `uc_` prefix. Use this value as `voice_id` in `POST /v1/text-to-speech` and other endpoints that accept `voice_id`." } }, "description": "Response of `POST /v1/voices/clone` — custom voice metadata returned by instant cloning." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "Segment discriminator. Always `pause`." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "Pause duration in seconds. Each pause may be up to 10 seconds; all pauses combined may be up to 60 seconds.", "exclusiveMinimum": 0 } }, "description": "A silent interval inserted without consuming credits.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single word-level alignment segment between the original transcript and the generated audio." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "Current subscription plan" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "Usage limit information" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "Credit usage information" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "Subscription information response model" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation and whitespace)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single character-level alignment segment between the original transcript and the generated audio." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." } }, "description": "Result returned after creating an instant or professional custom voice." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "Base64-encoded audio bytes. Decode and write to a file using the `audio_format` extension." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "Word-level timestamps (with attached punctuation). `null` when the request uses `granularity=char`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "Character-level timestamps (including punctuation and whitespace). `null` when the request uses `granularity=word`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "Audio encoding format of the bytes in `audio` — either `wav` or `mp3`, mirroring the request's `output.audio_format`." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "Length of the generated audio in seconds." } }, "description": "Response payload for POST /v1/text-to-speech/with-timestamps — base64-encoded audio plus per-word and per-character timestamps aligned with the generated speech." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "Audio sample. WAV or MP3, max 25 MB, 5-150 seconds.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "Voice name (1-30 characters)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "Engine model the voice is cloned for (`ssfm-v21` or `ssfm-v30`)." } }, "description": "Multipart request body for instant cloning." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "One WAV or MP3 recording, 25 MiB or less and 5 to 150 seconds long.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "Product Narrator", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." } }, "description": "Multipart request body for instant voice cloning." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "Custom Voice Name", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "Files", "description": "WAV or MP3 recordings used for training. Only one file can be uploaded.\r\n\r\n**Upload limits**\r\n\r\n* One WAV or MP3 file\r\n* File size: 1 GiB or less\r\n* Duration: 5 minutes to 3 hours\r\n* Sample rate: 16 kHz or higher\r\n\r\nFor best results, we recommend audio that meets the following conditions:\r\n\r\n* Record in a speaking style that closely matches how you want the generated voice to sound.\r\n* Record in a quiet environment without background noise.\r\n* Include only one speaker.\r\n* Record in the language specified in the `language` field.\r\n* Longer input audio results in higher-quality generated voices." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." }, "language": { "type": "string", "title": "Language", "example": "eng", "description": "ISO 639-3 language code, such as `kor` or `eng`." } }, "description": "Multipart request body for professional voice cloning." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "API key for authentication. You can obtain an API key from the Typecast API Console." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Text To Speech with Timestamps > Generate speech from text **and** return word/character-level timestamps aligned with the audio. Useful for subtitle sync, per-character highlight animation, and speech-region visualization. The request body matches the standard `/v1/text-to-speech` endpoint (voice_id, text, model, language, prompt, output, seed). Instead of raw audio bytes, this endpoint returns a JSON object containing base64-encoded audio plus `words` and `characters` arrays. Use the optional `granularity` query parameter to return only word-level or only character-level timestamps and reduce payload size. > **Language note.** For languages that do not use whitespace between words — such as Japanese (`jpn`) and Chinese (`zho`) — word-level alignment collapses the entire sentence into a single "word". For those languages, always request `granularity=char` to receive usable per-character timestamps. See [Listing all voices](/docs/api-reference/voices/list-voices) for available voices. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "Production server" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/text-to-speech/with-timestamps": { "post": { "tags": [ "Text-to-Speech" ], "x-mint": { "href": "/api-reference/text-to-speech/text-to-speech-with-timestamps" }, "summary": "Text To Speech with Timestamps", "security": [ { "ApiKeyAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TTSWithTimestampsResponse" }, "example": { "audio": "UklGRs...(base64-encoded audio omitted)", "words": [ { "end": 0.38, "text": "Try", "start": 0.08 }, { "end": 0.52, "text": "a", "start": 0.42 }, { "end": 1.26, "text": "5-minute", "start": 0.56 }, { "end": 1.88, "text": "stretch", "start": 1.3 }, { "end": 2.18, "text": "when", "start": 1.92 }, { "end": 2.42, "text": "you", "start": 2.22 }, { "end": 2.78, "text": "lose", "start": 2.46 }, { "end": 3.38, "text": "focus.", "start": 2.82 } ], "characters": [ { "end": 0.18, "text": "T", "start": 0.08 }, { "end": 0.27, "text": "r", "start": 0.18 }, { "end": 0.38, "text": "y", "start": 0.27 }, { "end": 0.42, "text": " ", "start": 0.38 }, { "end": 0.52, "text": "a", "start": 0.42 }, { "end": 0.56, "text": " ", "start": 0.52 }, { "end": 0.7, "text": "5", "start": 0.56 }, { "end": 0.76, "text": "-", "start": 0.7 }, { "end": 0.86, "text": "m", "start": 0.76 }, { "end": 0.94, "text": "i", "start": 0.86 }, { "end": 1.04, "text": "n", "start": 0.94 }, { "end": 1.12, "text": "u", "start": 1.04 }, { "end": 1.2, "text": "t", "start": 1.12 }, { "end": 1.26, "text": "e", "start": 1.2 }, { "end": 1.3, "text": " ", "start": 1.26 }, { "end": 1.4, "text": "s", "start": 1.3 }, { "end": 1.47, "text": "t", "start": 1.4 }, { "end": 1.56, "text": "r", "start": 1.47 }, { "end": 1.64, "text": "e", "start": 1.56 }, { "end": 1.72, "text": "t", "start": 1.64 }, { "end": 1.8, "text": "c", "start": 1.72 }, { "end": 1.88, "text": "h", "start": 1.8 }, { "end": 1.92, "text": " ", "start": 1.88 }, { "end": 2.02, "text": "w", "start": 1.92 }, { "end": 2.08, "text": "h", "start": 2.02 }, { "end": 2.14, "text": "e", "start": 2.08 }, { "end": 2.18, "text": "n", "start": 2.14 }, { "end": 2.22, "text": " ", "start": 2.18 }, { "end": 2.3, "text": "y", "start": 2.22 }, { "end": 2.38, "text": "o", "start": 2.3 }, { "end": 2.42, "text": "u", "start": 2.38 }, { "end": 2.46, "text": " ", "start": 2.42 }, { "end": 2.56, "text": "l", "start": 2.46 }, { "end": 2.64, "text": "o", "start": 2.56 }, { "end": 2.72, "text": "s", "start": 2.64 }, { "end": 2.78, "text": "e", "start": 2.72 }, { "end": 2.82, "text": " ", "start": 2.78 }, { "end": 2.92, "text": "f", "start": 2.82 }, { "end": 3.02, "text": "o", "start": 2.92 }, { "end": 3.12, "text": "c", "start": 3.02 }, { "end": 3.22, "text": "u", "start": 3.12 }, { "end": 3.32, "text": "s", "start": 3.22 }, { "end": 3.38, "text": ".", "start": 3.32 } ], "audio_format": "wav", "audio_duration": 3.38 } } }, "description": "Success - Returns base64 audio and timestamps" }, "400": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid voice_id" } } }, "description": "Bad Request - Invalid parameters" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Unauthorized - Authentication failed" }, "402": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Insufficient credit" } } }, "description": "Payment Required - Insufficient credits" }, "404": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Voice not found" } } }, "description": "Not Found - Voice model not available" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" } } }, "description": "Validation Error - The request is invalid or the input text cannot be synthesized" }, "429": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Too many requests" } } }, "description": "Too Many Requests - Rate limit exceeded" }, "500": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "An unexpected error occurred" } } }, "description": "Internal Server Error - TTS generation or timestamp alignment failed" } }, "deprecated": false, "parameters": [ { "in": "query", "name": "granularity", "schema": { "enum": [ "word", "char" ], "type": "string" }, "required": false, "description": "Filter for which timestamp arrays to return.\r\n\r\n* Omitted: returns both `words` and `characters`.\r\n* `word`: returns `words` only (`characters` is null).\r\n* `char`: returns `characters` only (`words` is null).\r\n\r\n**Languages without whitespace (e.g., `jpn`, `zho`):** `word` alignment yields a single segment covering the whole sentence, so use `char` to obtain meaningful timestamps." } ], "description": "Generate speech from text **and** return word/character-level timestamps aligned with the audio. Useful for subtitle sync, per-character highlight animation, and speech-region visualization.\r\n\r\nThe request body matches the standard `/v1/text-to-speech` endpoint (voice_id, text, model, language, prompt, output, seed). Instead of raw audio bytes, this endpoint returns a JSON object containing base64-encoded audio plus `words` and `characters` arrays.\r\n\r\nUse the optional `granularity` query parameter to return only word-level or only character-level timestamps and reduce payload size.\r\n\r\n> **Language note.** For languages that do not use whitespace between words — such as Japanese (`jpn`) and Chinese (`zho`) — word-level alignment collapses the entire sentence into a single \"word\". For those languages, always request `granularity=char` to receive usable per-character timestamps.\r\n\r\nSee [Listing all voices](/docs/api-reference/voices/list-voices) for available voices.", "operationId": "text_to_speech_with_timestamps_v1_text_to_speech_with_timestamps_post", "requestBody": { "content": { "application/json": { "schema": { "type": "object", "title": "TTSRequestWith-timestampsWith-timestamps", "required": [ "text", "model", "voice_id" ], "properties": { "seed": { "type": "integer", "anyOf": [ { "type": "integer", "maximum": 4294967295, "minimum": 0 }, { "type": "null" } ], "title": "Seed", "format": "uint32", "example": 42, "minimum": 0, "description": "Unsigned integer seed for reproducible speech generation. The same seed with the same input parameters will produce identical audio output.\r\n\r\n* Must be a non-negative integer (≥ 0). Negative values are not accepted.\r\n* If omitted, the server generates a random seed each time, producing slight variations." }, "text": { "type": "string", "title": "Text", "example": "Everything is so incredibly perfect that I feel like I'm dreaming.", "maxLength": 2000, "minLength": 1, "description": "Text to convert to speech. Minimum 1 character, maximum 2000 characters. Credits consumed based on text length. Supports multiple languages including English, Korean, Japanese, and Chinese. Special characters and punctuation are handled automatically." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "Voice model to use for speech synthesis.\r\n\r\n* **ssfm-v30**: Latest model with improved prosody and additional emotion presets (recommended)\r\n* **ssfm-v21**: Stable production model with reliable quality" }, "output": { "type": "object", "title": "", "properties": { "volume": { "anyOf": [ { "type": "integer", "maximum": 200, "minimum": 0 }, { "type": "null" } ], "title": "Volume", "example": 100, "description": "Adjusts the relative volume of the output audio: 0 (completely silent), 50 (half volume), 100 (standard volume, default), 150 (50% louder than standard), 200 (maximum volume, twice as loud as standard).\r\n\r\nSince this only scales the existing volume, using `volume` can amplify the loudness differences between voices if they have different baseline levels. For consistent output across all clips, use `target_lufs` instead.\r\n\r\n- **Note:** This parameter cannot be used simultaneously with the `target_lufs` parameter.\r\n\r\nRequired range: 0 <= x <= 200\r\n" }, "audio_pitch": { "type": "integer", "title": "Audio Pitch", "default": 0, "example": 0, "maximum": 12, "minimum": -12, "description": "Adjusts the pitch in semitones to affect perceived gender and age: -12 (one octave lower, deeper voice), -6 (half octave lower), 0 (original pitch, default), +6 (half octave higher), +12 (one octave higher, higher voice)" }, "audio_tempo": { "type": "number", "title": "Audio Tempo", "default": 1, "example": 1, "maximum": 2, "minimum": 0.5, "description": "Controls speech speed: 0.5 (half speed, very slow and clear), 0.75 (slightly slower than normal), 1.0 (normal speaking speed, default), 1.5 (50% faster than normal), 2.0 (double speed, very fast speech)" }, "target_lufs": { "type": "number", "anyOf": [ { "type": "number", "maximum": 0, "minimum": -70 }, { "type": "null" } ], "title": "Target Lufs", "example": -14, "description": "Sets the target absolute loudness (LUFS) for the output audio. This normalizes all generated voices to a consistent volume level, regardless of the original source's loudness. Values closer to 0 are louder, while values closer to -70 are quieter.\r\n\r\n- Required range: -70 <= x <= 0\r\n- Recommended values: -14 (common streaming standard), -23 (broadcast standard)\r\n- **Note:** This parameter cannot be used simultaneously with the `volume` parameter. Use `target_lufs` for consistent absolute loudness across different clips, or use `volume` for traditional relative scaling.\r\n" }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "default": "wav", "example": "wav", "description": "Output audio format.\r\n\r\n**WAV format:**\r\n- Uncompressed PCM audio\r\n- 16-bit depth, mono channel, 44100 Hz sample rate\r\n- Higher quality, larger file size\r\n- Recommended for professional audio production\r\n\r\n**MP3 format:**\r\n- Compressed MPEG Layer III audio\r\n- 320 kbps bitrate, 44100 Hz sample rate\r\n- Smaller file size\r\n- Recommended for web streaming and distribution\r\n" }, "remove_silence_ms": { "anyOf": [ { "type": "integer", "maximum": 1000, "minimum": 0 }, { "type": "null" } ], "title": "Remove Silence Ms", "default": null, "example": 100, "description": "When enabled, shortens detected silences longer than the specified duration to that duration. The value is in milliseconds (ms). The value is the duration of silence to retain, not the amount to remove.\r\n\r\n**Accepted values:**\r\n\r\n- An integer from **0 to 1000**, with a recommended range of 0 to 200.\r\n- Omitted or `null`: silence removal is disabled.\r\n- `0`: removes detected silence longer than 0 ms. **This does not disable the feature.**\r\n- Booleans, strings, fractional values, and out-of-range values are invalid.\r\n\r\n**Example:** `100` shortens detected silences longer than 100 ms to 100 ms. Lower values include shorter silence segments for removal and leave less silence in each affected segment." } }, "description": "Audio output settings including volume (0-200), pitch (-12 to +12 semitones), tempo (0.5x to 2.0x), and format (wav/mp3) for controlling the final audio characteristics\r\n\r\nUse `remove_silence_ms` (integer, 0–1000 ms) to shorten detected silence." }, "prompt": { "oneOf": [ { "$ref": "#/components/schemas/SmartPrompt" }, { "$ref": "#/components/schemas/PresetPrompt" }, { "$ref": "#/components/schemas/Prompt" } ], "title": "Prompt", "description": "Emotion and style settings for the generated speech, including emotion type (happy/sad/angry/normal) and intensity (0.0 to 2.0) to control the emotional expression" }, "language": { "type": "string", "title": "Language", "example": "eng", "description": "Language code following ISO 639-3 standard. Case-insensitive (both \"ENG\" and \"eng\" are accepted). If not provided, will be auto-detected based on text content.\r\n\r\n
\r\n ssfm-v30 Supported Languages (37)\r\n\r\n | Code | Language | Code | Language | Code | Language |\r\n | ---- | --------- | ---- | ---------- | ---- | ---------- |\r\n | ARA | Arabic | IND | Indonesian | POR | Portuguese |\r\n | BEN | Bengali | ITA | Italian | RON | Romanian |\r\n | BUL | Bulgarian | JPN | Japanese | RUS | Russian |\r\n | CES | Czech | KOR | Korean | SLK | Slovak |\r\n | DAN | Danish | MSA | Malay | SPA | Spanish |\r\n | DEU | German | NAN | Min Nan | SWE | Swedish |\r\n | ELL | Greek | NLD | Dutch | TAM | Tamil |\r\n | ENG | English | NOR | Norwegian | TGL | Tagalog |\r\n | FIN | Finnish | PAN | Punjabi | THA | Thai |\r\n | FRA | French | POL | Polish | TUR | Turkish |\r\n | HIN | Hindi | UKR | Ukrainian | VIE | Vietnamese |\r\n | HRV | Croatian | YUE | Cantonese | ZHO | Chinese |\r\n | HUN | Hungarian | | | | |\r\n
\r\n\r\n
\r\n ssfm-v21 Supported Languages (27)\r\n\r\n | Code | Language | Code | Language | Code | Language |\r\n | ---- | --------- | ---- | ---------- | ---- | --------- |\r\n | ARA | Arabic | IND | Indonesian | RON | Romanian |\r\n | BUL | Bulgarian | ITA | Italian | RUS | Russian |\r\n | CES | Czech | JPN | Japanese | SLK | Slovak |\r\n | DAN | Danish | KOR | Korean | SPA | Spanish |\r\n | DEU | German | MSA | Malay | SWE | Swedish |\r\n | ELL | Greek | NLD | Dutch | TAM | Tamil |\r\n | ENG | English | POL | Polish | TGL | Tagalog |\r\n | FIN | Finnish | POR | Portuguese | UKR | Ukrainian |\r\n | FRA | French | HRV | Croatian | ZHO | Chinese |\r\n
\r\n\r\n> **Timestamp endpoint note.** For languages without inter-word whitespace — Japanese (`jpn`) and Chinese (`zho`) — word-level alignment collapses the whole sentence into a single segment. Always pair these languages with `granularity=char` to receive usable per-character timestamps." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "Voice identifier. Two prefixes are supported:\r\n\r\n* `tc_` — Built-in Typecast voices (e.g., `tc_60e5426de8b95f1d3000d7b5`). See [Listing all voices](/docs/api-reference/voices/list-voices) for available IDs.\r\n* `uc_` — Custom voices created via [Instant cloning](/docs/api-reference/voices/instant-cloning) (e.g., `uc_64a1b2c3d4e5f6a7b8c9d0e1`). Only the owner of a cloned voice can use it.\r\n\r\nCase-sensitive: must use lowercase prefix." } }, "description": "Text-to-speech request parameters" } } }, "required": true, "description": "" }, "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request POST \\\r\n --url https://api.typecast.ai/v1/text-to-speech/with-timestamps \\\r\n --header 'Content-Type: application/json' \\\r\n --header 'X-API-KEY: ' \\\r\n --data @- <\",\r\n \"Content-Type\": \"application/json\",\r\n}\r\npayload = {\r\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\r\n \"text\": \"Try a 5-minute stretch when you lose focus.\",\r\n \"model\": \"ssfm-v30\",\r\n \"language\": \"eng\",\r\n \"prompt\": {\r\n \"emotion_type\": \"preset\",\r\n \"emotion_preset\": \"normal\",\r\n \"emotion_intensity\": 1.0,\r\n },\r\n}\r\n\r\nresponse = requests.post(\r\n f\"{API_HOST}/v1/text-to-speech/with-timestamps\",\r\n headers=headers,\r\n json=payload,\r\n timeout=60,\r\n)\r\nresponse.raise_for_status()\r\ndata = response.json()\r\n\r\nwith open(\"output.wav\", \"wb\") as f:\r\n f.write(base64.b64decode(data[\"audio\"]))\r\nprint(f\"Saved {len(data['audio'])} base64 chars; duration={data['audio_duration']}s\")\r\nfor w in (data.get(\"words\") or [])[:3]:\r\n print(f\" word: {w['text']!r} {w['start']:.3f}s - {w['end']:.3f}s\")\r\n" }, { "lang": "cURL", "label": "Silence removal", "source": "curl --request POST 'https://api.typecast.ai/v1/text-to-speech/with-timestamps' \\\r\n --header 'X-API-KEY: ' \\\r\n --header 'Content-Type: application/json' \\\r\n --output review.json \\\r\n --data-binary @- <<'JSON'\r\n{\r\n \"voice_id\": \"\",\r\n \"text\": \"Hello. Thank you for listening.\",\r\n \"model\": \"ssfm-v30\",\r\n \"output\": {\r\n \"audio_format\": \"wav\",\r\n \"remove_silence_ms\": 300\r\n }\r\n}\r\nJSON\r\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "Maximum number of concurrent requests allowed" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "Maximum number of custom voices (created via instant cloning) allowed on the current plan." }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "Usage limit information" }, "Prompt": { "title": "Prompt (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "Emotion preset to apply.\r\n\r\nSupported emotions for ssfm-v21: normal, happy, sad, angry\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "example": 1, "description": "Controls the strength of emotional expression (0.0 to 2.0).\r\n\r\n- 0.0: Completely neutral\r\n- 1.0: Standard expression (default)\r\n- 2.0: Maximum intensity\r\n" } }, "description": "Emotion and style settings for the generated speech." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "Age group classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **child**: Child voice (under 12 years old)\n- **teenager**: Teenage voice (13-19 years old)\n- **young_adult**: Young adult voice (20-35 years old)\n- **middle_age**: Middle-aged voice (36-60 years old)\n- **elder**: Elder voice (over 60 years old)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "Total monthly credits provided by the plan" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "Number of credits used" } }, "description": "Credit usage information" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group classification (child/teenager/young_adult/middle_age/elder)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender classification (male/female)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "List of supported TTS models with their available emotions (e.g., [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix (e.g., `tc_60e5426de8b95f1d3000d7b5`); cloned custom voices created via `POST /v1/voices/clone` use the `uc_` prefix and are also returned by `/v2/voices` for the owner." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "List of use case categories this voice is suitable for" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "Human-readable name of the voice" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type — `original` for Typecast-provided stock voices, `custom` for user-cloned voices." } }, "description": "V2 Voice response model with model-grouped emotions and enhanced metadata" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender (`male` or `female`)." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "Supported TTS models and their available emotions." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix and custom voices use the `uc_` prefix." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "Recommended use-case categories." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "Localized voice names, such as `{\"eng\": \"Daejin\", \"kor\": \"대진\"}`." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type (`original` or `custom`)." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "Preview audio URL when available." } }, "description": "Voice metadata with localized names and a preview audio URL." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "Subscription plan for the API.\n\nAvailable values:\n- **free**: Free tier with limited credits\n- **lite**: Lite plan with moderate credits\n- **plus**: Plus plan with higher credits\n- **custom**: Custom enterprise plan" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "TTS model version to use for speech synthesis. Different models offer varying capabilities and quality levels.\n\nAvailable models:\n- **ssfm-v30**: Latest model with improved prosody and additional emotion presets (recommended)\n- **ssfm-v21**: Stable production model with proven reliability and consistent quality\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS model version (e.g., ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "List of supported emotions for this model" } }, "description": "Model information including version and supported emotions" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "English voice name." }, "kor": { "type": "string", "title": "Kor", "description": "Korean voice name." } }, "description": "Localized voice names keyed by ISO 639-3 language code." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "Voice type classification.\n\n- `original` — Typecast-provided stock voices available to every account.\n- `custom` — Voices the user created by uploading or cloning their own sample." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "Gender classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **male**: Male voice\n- **female**: Female voice\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "Available emotion presets for speech synthesis. Each emotion affects the tone, pace, and expressiveness of the generated speech.\n\n**ssfm-v21 Supported Emotions (4 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n\n**ssfm-v30 Supported Emotions (7 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n- whisper: Soft, quiet speech\n- toneup: Higher tonal emphasis\n- tonedown: Lower tonal emphasis\n\nCheck available emotions for each voice through the /v2/voices API response.\n" }, "SmartPrompt": { "type": "object", "title": "SmartPrompt (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "Text that comes AFTER the `text` field in TTSRequest. Provides forward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model anticipate emotional transitions\r\n- Leave empty if no following context is available\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "Discriminator field to identify the prompt type. Must be set to \"smart\" for context-aware emotion inference.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "Text that comes BEFORE the `text` field in TTSRequest. Provides backward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model understand emotional build-up and context\r\n- Leave empty if no preceding context is available\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "PresetPrompt (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "Discriminator field to identify the prompt type. Must be set to \"preset\" for preset-based emotion control.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "Emotion preset to apply to the generated speech.\r\n\r\nSupported emotions: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "Controls the strength of emotional expression in the generated speech.\r\n\r\n- 0.0: Completely neutral, no emotional coloring\r\n- 0.5: Subtle emotional hints\r\n- 1.0: Standard emotional expression (default)\r\n- 1.5: Strong emotional emphasis\r\n- 2.0: Maximum intensity, highly expressive\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "Voice use case categories for content type filtering. Each voice is tagged with one or more use cases indicating its suitability for specific content types.\n\n**Available Use Cases:**\n- **Announcer**: Public announcements and presentations\n- **Anime**: Animation and character voices\n- **Audiobook**: Long-form narration and storytelling\n- **Conversational**: Chatbots and conversational AI\n- **Documentary**: Documentary narration and commentary\n- **E-learning**: Educational content and tutorials\n- **Rapper**: Rap and music performance\n- **Game**: Video game characters and narration\n- **Tiktok/Reels**: Short-form social media content\n- **News**: News broadcasting\n- **Podcast**: Broadcasting and podcast production\n- **Voicemail**: IVR systems and voice assistants\n- **Ads**: Advertising and promotional content\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "Error message describing the issue" }, "message": { "type": "string", "description": "Human-readable message for structured errors" }, "error_code": { "type": "string", "description": "Machine-readable error code for structured errors" } }, "description": "API errors use `detail` for legacy and validation errors, or `error_code` and `message` for structured errors." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "Failure reason when `status` is `failed`." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "source": { "type": "string", "title": "Source", "description": "Creation method (`instant` or `professional`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "Creation time in UTC." } }, "description": "A custom voice owned by the authenticated account." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "Recommendation relevance score. Higher values indicate a stronger match for the query." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "Typecast voice identifier with the `tc_` prefix. Use this value as `voice_id` in text-to-speech requests." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "Human-readable voice name." } }, "description": "Recommended voice candidate returned by `GET /v1/voices/recommendations`, sorted by relevance." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "Current creation or training status of a custom voice." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Human-readable voice name (1-30 characters)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "Engine model the voice was cloned for (`ssfm-v21` or `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "Custom voice identifier with the `uc_` prefix. Use this value as `voice_id` in `POST /v1/text-to-speech` and other endpoints that accept `voice_id`." } }, "description": "Response of `POST /v1/voices/clone` — custom voice metadata returned by instant cloning." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "Segment discriminator. Always `pause`." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "Pause duration in seconds. Each pause may be up to 10 seconds; all pauses combined may be up to 60 seconds.", "exclusiveMinimum": 0 } }, "description": "A silent interval inserted without consuming credits.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single word-level alignment segment between the original transcript and the generated audio." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "Current subscription plan" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "Usage limit information" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "Credit usage information" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "Subscription information response model" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation and whitespace)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single character-level alignment segment between the original transcript and the generated audio." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." } }, "description": "Result returned after creating an instant or professional custom voice." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "Base64-encoded audio bytes. Decode and write to a file using the `audio_format` extension." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "Word-level timestamps (with attached punctuation). `null` when the request uses `granularity=char`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "Character-level timestamps (including punctuation and whitespace). `null` when the request uses `granularity=word`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "Audio encoding format of the bytes in `audio` — either `wav` or `mp3`, mirroring the request's `output.audio_format`." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "Length of the generated audio in seconds." } }, "description": "Response payload for POST /v1/text-to-speech/with-timestamps — base64-encoded audio plus per-word and per-character timestamps aligned with the generated speech." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "Audio sample. WAV or MP3, max 25 MB, 5-150 seconds.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "Voice name (1-30 characters)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "Engine model the voice is cloned for (`ssfm-v21` or `ssfm-v30`)." } }, "description": "Multipart request body for instant cloning." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "One WAV or MP3 recording, 25 MiB or less and 5 to 150 seconds long.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "Product Narrator", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." } }, "description": "Multipart request body for instant voice cloning." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "Custom Voice Name", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "Files", "description": "WAV or MP3 recordings used for training. Only one file can be uploaded.\r\n\r\n**Upload limits**\r\n\r\n* One WAV or MP3 file\r\n* File size: 1 GiB or less\r\n* Duration: 5 minutes to 3 hours\r\n* Sample rate: 16 kHz or higher\r\n\r\nFor best results, we recommend audio that meets the following conditions:\r\n\r\n* Record in a speaking style that closely matches how you want the generated voice to sound.\r\n* Record in a quiet environment without background noise.\r\n* Include only one speaker.\r\n* Record in the language specified in the `language` field.\r\n* Longer input audio results in higher-quality generated voices." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." }, "language": { "type": "string", "title": "Language", "example": "eng", "description": "ISO 639-3 language code, such as `kor` or `eng`." } }, "description": "Multipart request body for professional voice cloning." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "API key for authentication. You can obtain an API key from the Typecast API Console." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Streaming Text To Speech > Generate speech from text using real-time streaming, allowing audio playback to begin before the entire synthesis is complete. This endpoint streams audio data in chunks, enabling low-latency audio playback for applications requiring immediate feedback. **Streaming Format:** * **WAV format**: First chunk contains WAV header (size\=0xFFFFFFFF for streaming) followed by raw PCM data. Subsequent chunks contain only PCM data. * **MP3 format**: Each chunk contains post-processed MP3 data that can be decoded independently. **Use Cases:** * Conversational AI, chatbots and real-time voice assistants * Interactive applications requiring immediate audio feedback * Long-form content where waiting for full synthesis is impractical **Request Parameters:** Uses the same TTSRequest schema as the standard TTS endpoint. Set `output.audio_format` to "wav" or "mp3" to control the streaming format. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "Production server" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/text-to-speech/stream": { "post": { "tags": [ "Text-to-Speech" ], "x-mint": { "href": "/api-reference/text-to-speech/streaming-text-to-speech" }, "summary": "Streaming Text To Speech", "security": [ { "ApiKeyAuth": [] } ], "responses": { "200": { "content": { "audio/wav": { "schema": { "type": "string", "format": "binary", "description": "Chunked WAV audio stream (16-bit, mono, 32000 Hz). First chunk includes WAV header with size 0xFFFFFFFF (indicating streaming), followed by raw PCM data. Subsequent chunks contain only PCM data." }, "example": "[Binary audio stream - WAV chunks]" } }, "description": "Success - Returns streaming audio data in chunks" }, "400": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid voice_id" } } }, "description": "Bad Request - Invalid parameters" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Unauthorized - Authentication failed" }, "402": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Insufficient credit" } } }, "description": "Payment Required - Insufficient credits" }, "404": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Voice not found" } } }, "description": "Not Found - Voice model not available" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" } } }, "description": "Validation Error - The request is invalid or the input text cannot be synthesized.\r\nInput errors detected before streaming starts return `TEXT_NOT_SYNTHESIZABLE`. After the streaming response has started, its HTTP status can no longer be changed." }, "429": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Too many requests" } } }, "description": "Too Many Requests - Rate limit exceeded" }, "500": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "An unexpected error occurred" } } }, "description": "Internal Server Error - Server processing failed" } }, "deprecated": false, "description": "Generate speech from text using real-time streaming, allowing audio playback to begin before the entire synthesis is complete.\n\nThis endpoint streams audio data in chunks, enabling low-latency audio playback for applications requiring immediate feedback.\n\n**Streaming Format:**\n\n* **WAV format**: First chunk contains WAV header (size\\=0xFFFFFFFF for streaming) followed by raw PCM data. Subsequent chunks contain only PCM data.\n* **MP3 format**: Each chunk contains post-processed MP3 data that can be decoded independently.\n\n**Use Cases:**\n\n* Conversational AI, chatbots and real-time voice assistants\n* Interactive applications requiring immediate audio feedback\n* Long-form content where waiting for full synthesis is impractical\n\n**Request Parameters:**\nUses the same TTSRequest schema as the standard TTS endpoint. Set `output.audio_format` to \"wav\" or \"mp3\" to control the streaming format.", "operationId": "text_to_speech_stream_v1_text_to_speech_stream_post", "requestBody": { "content": { "application/json": { "schema": { "type": "object", "title": "TTSRequestStreamStream", "required": [ "voice_id", "text", "model" ], "properties": { "seed": { "type": "integer", "anyOf": [ { "type": "integer", "maximum": 4294967295, "minimum": 0 }, { "type": "null" } ], "title": "Seed", "format": "uint32", "example": 42, "minimum": 0, "description": "Unsigned integer seed for reproducible speech generation. The same seed with the same input parameters will produce identical audio output.\r\n\r\n* Must be a non-negative integer (≥ 0). Negative values are not accepted.\r\n* If omitted, the server generates a random seed each time, producing slight variations." }, "text": { "type": "string", "title": "Text", "example": "Everything is so incredibly perfect that I feel like I'm dreaming.", "maxLength": 2000, "minLength": 1, "description": "Text to convert to speech. Minimum 1 character, maximum 2000 characters. Credits consumed based on text length. Supports multiple languages including English, Korean, Japanese, and Chinese. Special characters and punctuation are handled automatically." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "Voice model to use for speech synthesis.\r\n\r\n* **ssfm-v30**: Latest model with improved prosody and additional emotion presets (recommended)\r\n* **ssfm-v21**: Stable production model with reliable quality" }, "output": { "type": "object", "title": "", "properties": { "audio_pitch": { "type": "integer", "title": "Audio Pitch", "default": 0, "example": 0, "maximum": 12, "minimum": -12, "description": "Adjusts the pitch in semitones to affect perceived gender and age: -12 (one octave lower, deeper voice), -6 (half octave lower), 0 (original pitch, default), +6 (half octave higher), +12 (one octave higher, higher voice)" }, "audio_tempo": { "type": "number", "title": "Audio Tempo", "default": 1, "example": 1, "maximum": 2, "minimum": 0.5, "description": "Controls speech speed: 0.5 (half speed, very slow and clear), 0.75 (slightly slower than normal), 1.0 (normal speaking speed, default), 1.5 (50% faster than normal), 2.0 (double speed, very fast speech)" }, "target_lufs": { "type": "number", "anyOf": [ { "type": "number", "maximum": 0, "minimum": -70 }, { "type": "null" } ], "title": "Target Lufs", "example": -14, "description": "Sets the target absolute loudness (LUFS) for streaming output audio. This normalizes generated audio to a consistent loudness regardless of the original source. Cannot be used with the `volume` parameter.\r\n\r\nRecommended values: -14 (common streaming standard), -23 (broadcast standard).\r\n" }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "default": "wav", "example": "wav", "description": "Output audio format for streaming.\r\n\r\n**WAV format:**\r\n- Uncompressed PCM audio\r\n- 16-bit depth, mono channel, **32000 Hz** sample rate\r\n- Chunked transfer: first chunk contains the WAV header (size = 0xFFFFFFFF), subsequent chunks contain raw PCM data\r\n- Recommended when you want to play audio as it arrives\r\n\r\n**MP3 format:**\r\n- Compressed MPEG Layer III audio\r\n- 320 kbps bitrate, 44100 Hz sample rate\r\n- Chunked transfer: each chunk contains independently decodable MPEG frames\r\n- Recommended for bandwidth-constrained clients\r\n" }, "remove_silence_ms": { "anyOf": [ { "type": "integer", "maximum": 1000, "minimum": 0 }, { "type": "null" } ], "title": "Remove Silence Ms", "default": null, "example": 100, "description": "When enabled, shortens detected silences longer than the specified duration to that duration. The value is in milliseconds (ms). The value is the duration of silence to retain, not the amount to remove.\r\n\r\n**Accepted values:**\r\n\r\n- An integer from **0 to 1000**, with a recommended range of 0 to 200.\r\n- Omitted or `null`: silence removal is disabled.\r\n- `0`: removes detected silence longer than 0 ms. **This does not disable the feature.**\r\n- Booleans, strings, fractional values, and out-of-range values are invalid.\r\n\r\n**Example:** `100` shortens detected silences longer than 100 ms to 100 ms. Lower values include shorter silence segments for removal and leave less silence in each affected segment." } }, "description": "Streaming audio output settings including pitch (-12 to +12 semitones), tempo (0.5x to 2.0x), format (wav/mp3), and target\\_lufs (-70 to 0 LUFS). Note: volume is not available in streaming mode.\r\n\r\nAlso supports `remove_silence_ms` (integer, 0–1000 ms) for silence removal." }, "prompt": { "oneOf": [ { "$ref": "#/components/schemas/SmartPrompt" }, { "$ref": "#/components/schemas/PresetPrompt" }, { "$ref": "#/components/schemas/Prompt" } ], "title": "Prompt", "description": "Emotion and style settings for the generated speech, including emotion type (happy/sad/angry/normal) and intensity (0.0 to 2.0) to control the emotional expression" }, "language": { "type": "string", "title": "Language", "example": "eng", "description": "Language code following ISO 639-3 standard. Case-insensitive (both \"ENG\" and \"eng\" are accepted). If not provided, will be auto-detected based on text content.\r\n\r\n
\r\n ssfm-v30 Supported Languages (37)\r\n\r\n | Code | Language | Code | Language | Code | Language |\r\n | ---- | --------- | ---- | ---------- | ---- | ---------- |\r\n | ARA | Arabic | IND | Indonesian | POR | Portuguese |\r\n | BEN | Bengali | ITA | Italian | RON | Romanian |\r\n | BUL | Bulgarian | JPN | Japanese | RUS | Russian |\r\n | CES | Czech | KOR | Korean | SLK | Slovak |\r\n | DAN | Danish | MSA | Malay | SPA | Spanish |\r\n | DEU | German | NAN | Min Nan | SWE | Swedish |\r\n | ELL | Greek | NLD | Dutch | TAM | Tamil |\r\n | ENG | English | NOR | Norwegian | TGL | Tagalog |\r\n | FIN | Finnish | PAN | Punjabi | THA | Thai |\r\n | FRA | French | POL | Polish | TUR | Turkish |\r\n | HIN | Hindi | UKR | Ukrainian | VIE | Vietnamese |\r\n | HRV | Croatian | YUE | Cantonese | ZHO | Chinese |\r\n | HUN | Hungarian | | | | |\r\n
\r\n\r\n
\r\n ssfm-v21 Supported Languages (27)\r\n\r\n | Code | Language | Code | Language | Code | Language |\r\n | ---- | --------- | ---- | ---------- | ---- | --------- |\r\n | ARA | Arabic | IND | Indonesian | RON | Romanian |\r\n | BUL | Bulgarian | ITA | Italian | RUS | Russian |\r\n | CES | Czech | JPN | Japanese | SLK | Slovak |\r\n | DAN | Danish | KOR | Korean | SPA | Spanish |\r\n | DEU | German | MSA | Malay | SWE | Swedish |\r\n | ELL | Greek | NLD | Dutch | TAM | Tamil |\r\n | ENG | English | POL | Polish | TGL | Tagalog |\r\n | FIN | Finnish | POR | Portuguese | UKR | Ukrainian |\r\n | FRA | French | HRV | Croatian | ZHO | Chinese |\r\n
" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "Voice identifier. Two prefixes are supported:\r\n\r\n* `tc_` — Built-in Typecast voices (e.g., `tc_60e5426de8b95f1d3000d7b5`). See [Listing all voices](/docs/api-reference/voices/list-voices) for available IDs.\r\n* `uc_` — Custom voices created via [Instant cloning](/docs/api-reference/voices/instant-cloning) (e.g., `uc_64a1b2c3d4e5f6a7b8c9d0e1`). Only the owner of a cloned voice can use it.\r\n\r\nCase-sensitive: must use lowercase prefix." } }, "description": "Text-to-speech streaming request parameters" } } }, "required": true, "description": "" }, "x-codeSamples": [ { "lang": "cURL", "label": "cURL (stream + play)", "source": "# Pipe streaming audio directly into ffplay for real-time playback.\n# Requires: ffmpeg (brew/choco/apt install ffmpeg)\ncurl -N -s --request POST \\\n --url https://api.typecast.ai/v1/text-to-speech/stream \\\n --header 'Content-Type: application/json' \\\n --header 'X-API-KEY: ' \\\n --data @- <\", \"Content-Type\": \"application/json\"}\npayload = {\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\": \"Thanks for reaching out. Your reservation has been confirmed for Friday at 7 PM.\",\n \"model\": \"ssfm-v30\",\n}\n\nresp = requests.post(\n f\"{API_HOST}/v1/text-to-speech/stream\",\n headers=headers, json=payload, stream=True, timeout=60,\n)\nresp.raise_for_status()\n\nwith sd.RawOutputStream(samplerate=32000, channels=1, dtype=\"int16\") as player:\n buf, first = bytearray(), True\n for chunk in resp.iter_content(chunk_size=4096):\n if not chunk:\n continue\n if first:\n chunk = chunk[44:] # strip WAV header\n first = False\n buf.extend(chunk)\n # Write 2-byte-aligned slices (int16 samples).\n n = len(buf) - (len(buf) % 2)\n if n:\n player.write(bytes(buf[:n]))\n del buf[:n]\n\nprint(\"Playback completed\")\n" }, { "lang": "C#", "label": "C# (HttpClient + ffplay)", "source": "// Real-time playback by piping the stream into ffplay.\n// Requires: ffmpeg (brew/choco/apt install ffmpeg)\nusing System;\nusing System.Diagnostics;\nusing System.Net.Http;\nusing System.Text;\nusing System.Threading.Tasks;\n\nvar client = new HttpClient();\nclient.DefaultRequestHeaders.Add(\"X-API-KEY\", \"\");\n\nvar requestBody = @\"{\n \"\"voice_id\"\": \"\"tc_60e5426de8b95f1d3000d7b5\"\",\n \"\"text\"\": \"\"Thanks for reaching out. Your reservation has been confirmed for Friday at 7 PM.\"\",\n \"\"model\"\": \"\"ssfm-v30\"\"\n}\";\n\nvar ffplay = new Process\n{\n StartInfo = new ProcessStartInfo\n {\n FileName = \"ffplay\",\n Arguments = \"-autoexit -nodisp -loglevel error -i pipe:0\",\n RedirectStandardInput = true,\n UseShellExecute = false,\n }\n};\nffplay.Start();\n\nvar request = new HttpRequestMessage(HttpMethod.Post, \"https://api.typecast.ai/v1/text-to-speech/stream\")\n{\n Content = new StringContent(requestBody, Encoding.UTF8, \"application/json\")\n};\n\n// ResponseHeadersRead enables true streaming (avoids full buffering).\nusing var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);\nresponse.EnsureSuccessStatusCode();\nusing var stream = await response.Content.ReadAsStreamAsync();\nawait stream.CopyToAsync(ffplay.StandardInput.BaseStream);\nffplay.StandardInput.Close();\nawait ffplay.WaitForExitAsync();\n" }, { "lang": "Kotlin", "label": "Kotlin (OkHttp + ffplay)", "source": "// Real-time playback by piping the OkHttp response stream into ffplay.\n// Requires: ffmpeg (brew/choco/apt install ffmpeg)\n// For Android, replace the ffplay Process with AudioTrack + raw PCM feed.\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.RequestBody.Companion.toRequestBody\n\nval ffplay = ProcessBuilder(\n \"ffplay\", \"-autoexit\", \"-nodisp\", \"-loglevel\", \"error\", \"-i\", \"pipe:0\"\n).redirectError(ProcessBuilder.Redirect.DISCARD).start()\n\nval client = OkHttpClient()\nval body = \"\"\"\n{\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\": \"Thanks for reaching out. Your reservation has been confirmed for Friday at 7 PM.\",\n \"model\": \"ssfm-v30\"\n}\n\"\"\".trimIndent().toRequestBody(\"application/json\".toMediaType())\n\nval request = Request.Builder()\n .url(\"https://api.typecast.ai/v1/text-to-speech/stream\")\n .addHeader(\"X-API-KEY\", \"\")\n .post(body)\n .build()\n\nclient.newCall(request).execute().use { response ->\n response.body?.byteStream()?.use { input -> input.copyTo(ffplay.outputStream) }\n}\nffplay.outputStream.close()\nffplay.waitFor()\n" }, { "lang": "C++", "label": "C++ (libcurl + ffplay)", "source": "// Real-time playback: libcurl write callback pipes each chunk into\n// ffplay via popen. Requires: ffmpeg (brew/choco/apt install ffmpeg)\n#include \n#include \n#include \n\nstatic FILE* player = nullptr;\n\nsize_t cb(void* ptr, size_t size, size_t nmemb, void*) {\n return fwrite(ptr, size, nmemb, player);\n}\n\nint main() {\n player = popen(\"ffplay -autoexit -nodisp -loglevel error -i pipe:0\", \"w\");\n\n CURL* curl = curl_easy_init();\n struct curl_slist* headers = nullptr;\n headers = curl_slist_append(headers, \"Content-Type: application/json\");\n headers = curl_slist_append(headers, \"X-API-KEY: \");\n\n std::string body = R\"({\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\": \"Thanks for reaching out. Your reservation has been confirmed for Friday at 7 PM.\",\n \"model\": \"ssfm-v30\"\n })\";\n\n curl_easy_setopt(curl, CURLOPT_URL, \"https://api.typecast.ai/v1/text-to-speech/stream\");\n curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);\n curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());\n curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cb);\n\n curl_easy_perform(curl);\n\n curl_slist_free_all(headers);\n curl_easy_cleanup(curl);\n pclose(player);\n return 0;\n}\n" }, { "lang": "C", "label": "C (libcurl + ffplay)", "source": "/* Real-time playback: libcurl write callback pipes each chunk into\n * ffplay via popen. Requires: ffmpeg (brew/choco/apt install ffmpeg) */\n#include \n#include \n\nstatic FILE* player = NULL;\n\nsize_t cb(void* ptr, size_t size, size_t nmemb, void* ud) {\n (void)ud;\n return fwrite(ptr, size, nmemb, player);\n}\n\nint main(void) {\n player = popen(\"ffplay -autoexit -nodisp -loglevel error -i pipe:0\", \"w\");\n\n curl_global_init(CURL_GLOBAL_ALL);\n CURL* curl = curl_easy_init();\n\n struct curl_slist* headers = NULL;\n headers = curl_slist_append(headers, \"Content-Type: application/json\");\n headers = curl_slist_append(headers, \"X-API-KEY: \");\n\n const char* body =\n \"{\"\n \"\\\"voice_id\\\":\\\"tc_60e5426de8b95f1d3000d7b5\\\",\"\n \"\\\"text\\\":\\\"Thanks for reaching out. Your reservation has been confirmed for Friday at 7 PM.\\\",\"\n \"\\\"model\\\":\\\"ssfm-v30\\\"\"\n \"}\";\n\n curl_easy_setopt(curl, CURLOPT_URL, \"https://api.typecast.ai/v1/text-to-speech/stream\");\n curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);\n curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);\n curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cb);\n\n curl_easy_perform(curl);\n\n curl_slist_free_all(headers);\n curl_easy_cleanup(curl);\n curl_global_cleanup();\n pclose(player);\n return 0;\n}\n" }, { "lang": "Swift", "label": "Swift (URLSession + ffplay)", "source": "// Real-time playback (macOS): pipe URLSession bytes into ffplay via\n// Process. Requires: ffmpeg (brew install ffmpeg).\n// Requires iOS 15 / macOS 12 for URLSession.bytes(for:).\n// Compile with: swiftc -parse-as-library main.swift -o streaming_tts\n// For iOS, replace Process/ffplay with AVAudioEngine + scheduled PCM buffers.\nimport Foundation\n\n@main\nstruct StreamingTTS {\n static func main() async throws {\n let ffplay = Process()\n ffplay.executableURL = URL(fileURLWithPath: \"/usr/bin/env\")\n ffplay.arguments = [\"ffplay\", \"-autoexit\", \"-nodisp\", \"-loglevel\", \"error\", \"-i\", \"pipe:0\"]\n let pipe = Pipe()\n ffplay.standardInput = pipe\n try ffplay.run()\n\n var request = URLRequest(url: URL(string: \"https://api.typecast.ai/v1/text-to-speech/stream\")!)\n request.httpMethod = \"POST\"\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.setValue(\"\", forHTTPHeaderField: \"X-API-KEY\")\n request.httpBody = try JSONSerialization.data(withJSONObject: [\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\": \"Thanks for reaching out. Your reservation has been confirmed for Friday at 7 PM.\",\n \"model\": \"ssfm-v30\",\n ])\n\n let (bytes, _) = try await URLSession.shared.bytes(for: request)\n var buffer = Data()\n buffer.reserveCapacity(4096)\n for try await byte in bytes {\n buffer.append(byte)\n if buffer.count >= 4096 {\n try pipe.fileHandleForWriting.write(contentsOf: buffer)\n buffer.removeAll(keepingCapacity: true)\n }\n }\n if !buffer.isEmpty {\n try pipe.fileHandleForWriting.write(contentsOf: buffer)\n }\n try pipe.fileHandleForWriting.close()\n ffplay.waitUntilExit()\n }\n}\n" }, { "lang": "Rust", "label": "Rust (reqwest + ffplay)", "source": "// Real-time playback: pipe reqwest stream into ffplay via tokio Command.\n// Requires: ffmpeg (brew/choco/apt install ffmpeg)\n// Cargo.toml:\n// reqwest = { version = \"0.12\", features = [\"json\", \"stream\"] }\n// tokio = { version = \"1\", features = [\"full\"] }\n// serde_json = \"1\"\nuse reqwest;\nuse serde_json::json;\nuse std::process::Stdio;\nuse tokio::io::AsyncWriteExt;\nuse tokio::process::Command;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box> {\n let mut ffplay = Command::new(\"ffplay\")\n .args([\"-autoexit\", \"-nodisp\", \"-loglevel\", \"error\", \"-i\", \"pipe:0\"])\n .stdin(Stdio::piped())\n .spawn()?;\n let mut stdin = ffplay.stdin.take().expect(\"failed to open ffplay stdin\");\n\n let client = reqwest::Client::new();\n let body = json!({\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\": \"Thanks for reaching out. Your reservation has been confirmed for Friday at 7 PM.\",\n \"model\": \"ssfm-v30\"\n });\n\n let mut response = client\n .post(\"https://api.typecast.ai/v1/text-to-speech/stream\")\n .header(\"X-API-KEY\", \"\")\n .header(\"Content-Type\", \"application/json\")\n .json(&body)\n .send()\n .await?;\n\n while let Some(chunk) = response.chunk().await? {\n stdin.write_all(&chunk).await?;\n }\n drop(stdin);\n ffplay.wait().await?;\n Ok(())\n}\n" }, { "lang": "JavaScript", "label": "JavaScript (Node.js + ffplay)", "source": "// Node 18+ (built-in fetch). Pipe streamed audio into ffplay.\n// Requires: ffmpeg (brew/choco/apt install ffmpeg)\nimport { spawn } from \"node:child_process\";\n\nconst ffplay = spawn(\n \"ffplay\",\n [\"-autoexit\", \"-nodisp\", \"-loglevel\", \"error\", \"-i\", \"pipe:0\"],\n { stdio: [\"pipe\", \"ignore\", \"ignore\"] },\n);\n\nconst response = await fetch(\"https://api.typecast.ai/v1/text-to-speech/stream\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-KEY\": \"\",\n },\n body: JSON.stringify({\n voice_id: \"tc_60e5426de8b95f1d3000d7b5\",\n text: \"Thanks for reaching out. Your reservation has been confirmed for Friday at 7 PM.\",\n model: \"ssfm-v30\",\n }),\n});\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\n\n// fetch().body is a Web ReadableStream — read chunks as they arrive.\nconst reader = response.body.getReader();\nwhile (true) {\n const { value, done } = await reader.read();\n if (done) break;\n ffplay.stdin.write(value);\n}\nffplay.stdin.end();\nawait new Promise((resolve) => ffplay.on(\"close\", resolve));\n" }, { "lang": "PHP", "label": "PHP (curl + ffplay)", "source": " \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\" => \"Thanks for reaching out. Your reservation has been confirmed for Friday at 7 PM.\",\n \"model\" => \"ssfm-v30\",\n]);\n\n$ch = curl_init(\"https://api.typecast.ai/v1/text-to-speech/stream\");\ncurl_setopt_array($ch, [\n CURLOPT_POST => true,\n CURLOPT_HTTPHEADER => [\n \"Content-Type: application/json\",\n \"X-API-KEY: \",\n ],\n CURLOPT_POSTFIELDS => $payload,\n CURLOPT_WRITEFUNCTION => function ($ch, $data) use ($ffplay) {\n fwrite($ffplay, $data);\n return strlen($data);\n },\n]);\ncurl_exec($ch);\npclose($ffplay);\n" }, { "lang": "Go", "label": "Go (net/http + ffplay)", "source": "// Pipes the streaming response body into ffplay's stdin.\n// Requires: ffmpeg (brew/choco/apt install ffmpeg)\npackage main\n\nimport (\n \"bytes\"\n \"io\"\n \"net/http\"\n \"os/exec\"\n)\n\nfunc main() {\n ffplay := exec.Command(\"ffplay\", \"-autoexit\", \"-nodisp\", \"-loglevel\", \"error\", \"-i\", \"pipe:0\")\n stdin, _ := ffplay.StdinPipe()\n if err := ffplay.Start(); err != nil {\n panic(err)\n }\n\n body := []byte(`{\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\": \"Thanks for reaching out. Your reservation has been confirmed for Friday at 7 PM.\",\n \"model\": \"ssfm-v30\"\n }`)\n\n req, _ := http.NewRequest(\"POST\", \"https://api.typecast.ai/v1/text-to-speech/stream\", bytes.NewReader(body))\n req.Header.Set(\"Content-Type\", \"application/json\")\n req.Header.Set(\"X-API-KEY\", \"\")\n\n resp, err := http.DefaultClient.Do(req)\n if err != nil {\n panic(err)\n }\n defer resp.Body.Close()\n\n io.Copy(stdin, resp.Body)\n stdin.Close()\n ffplay.Wait()\n}\n" }, { "lang": "Java", "label": "Java (HttpClient + ffplay)", "source": "// Java 11+ HttpClient with InputStream body handler.\n// Pipes the streaming response into ffplay's stdin.\n// Requires: ffmpeg (brew/choco/apt install ffmpeg)\nimport java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\nimport java.io.InputStream;\nimport java.io.OutputStream;\n\npublic class StreamingTTS {\n public static void main(String[] args) throws Exception {\n Process ffplay = new ProcessBuilder(\n \"ffplay\", \"-autoexit\", \"-nodisp\", \"-loglevel\", \"error\", \"-i\", \"pipe:0\")\n .redirectError(ProcessBuilder.Redirect.DISCARD)\n .start();\n\n String body = \"\"\"\n {\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\": \"Thanks for reaching out. Your reservation has been confirmed for Friday at 7 PM.\",\n \"model\": \"ssfm-v30\"\n }\n \"\"\";\n\n HttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.typecast.ai/v1/text-to-speech/stream\"))\n .header(\"Content-Type\", \"application/json\")\n .header(\"X-API-KEY\", \"\")\n .POST(HttpRequest.BodyPublishers.ofString(body))\n .build();\n\n HttpResponse response = HttpClient.newHttpClient()\n .send(request, HttpResponse.BodyHandlers.ofInputStream());\n\n try (InputStream in = response.body();\n OutputStream out = ffplay.getOutputStream()) {\n in.transferTo(out);\n }\n ffplay.waitFor();\n }\n}\n" }, { "lang": "Ruby", "label": "Ruby (net/http + ffplay)", "source": "# Pipes the streaming response into ffplay via IO.popen.\n# Requires: ffmpeg (brew/choco/apt install ffmpeg)\nrequire \"net/http\"\nrequire \"uri\"\nrequire \"json\"\n\nffplay = IO.popen(\n [\"ffplay\", \"-autoexit\", \"-nodisp\", \"-loglevel\", \"error\", \"-i\", \"pipe:0\"],\n \"wb\",\n)\n\nuri = URI(\"https://api.typecast.ai/v1/text-to-speech/stream\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nreq = Net::HTTP::Post.new(uri)\nreq[\"Content-Type\"] = \"application/json\"\nreq[\"X-API-KEY\"] = \"\"\nreq.body = {\n voice_id: \"tc_60e5426de8b95f1d3000d7b5\",\n text: \"Thanks for reaching out. Your reservation has been confirmed for Friday at 7 PM.\",\n model: \"ssfm-v30\",\n}.to_json\n\nhttp.request(req) do |response|\n response.read_body { |chunk| ffplay.write(chunk) }\nend\n\nffplay.close\n" }, { "lang": "cURL", "label": "Silence removal", "source": "curl --no-buffer --request POST 'https://api.typecast.ai/v1/text-to-speech/stream' \\\n --header 'X-API-KEY: ' \\\n --header 'Content-Type: application/json' \\\n --output review.wav \\\n --data-binary @- <<'JSON'\n{\n \"voice_id\": \"\",\n \"text\": \"Hello. Thank you for listening.\",\n \"model\": \"ssfm-v30\",\n \"output\": {\n \"audio_format\": \"wav\",\n \"remove_silence_ms\": 300\n }\n}\nJSON\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "Maximum number of concurrent requests allowed" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "Maximum number of custom voices (created via instant cloning) allowed on the current plan." }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "Usage limit information" }, "Prompt": { "title": "Prompt (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "Emotion preset to apply.\r\n\r\nSupported emotions for ssfm-v21: normal, happy, sad, angry\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "example": 1, "description": "Controls the strength of emotional expression (0.0 to 2.0).\r\n\r\n- 0.0: Completely neutral\r\n- 1.0: Standard expression (default)\r\n- 2.0: Maximum intensity\r\n" } }, "description": "Emotion and style settings for the generated speech." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "Age group classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **child**: Child voice (under 12 years old)\n- **teenager**: Teenage voice (13-19 years old)\n- **young_adult**: Young adult voice (20-35 years old)\n- **middle_age**: Middle-aged voice (36-60 years old)\n- **elder**: Elder voice (over 60 years old)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "Total monthly credits provided by the plan" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "Number of credits used" } }, "description": "Credit usage information" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group classification (child/teenager/young_adult/middle_age/elder)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender classification (male/female)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "List of supported TTS models with their available emotions (e.g., [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix (e.g., `tc_60e5426de8b95f1d3000d7b5`); cloned custom voices created via `POST /v1/voices/clone` use the `uc_` prefix and are also returned by `/v2/voices` for the owner." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "List of use case categories this voice is suitable for" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "Human-readable name of the voice" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type — `original` for Typecast-provided stock voices, `custom` for user-cloned voices." } }, "description": "V2 Voice response model with model-grouped emotions and enhanced metadata" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender (`male` or `female`)." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "Supported TTS models and their available emotions." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix and custom voices use the `uc_` prefix." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "Recommended use-case categories." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "Localized voice names, such as `{\"eng\": \"Daejin\", \"kor\": \"대진\"}`." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type (`original` or `custom`)." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "Preview audio URL when available." } }, "description": "Voice metadata with localized names and a preview audio URL." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "Subscription plan for the API.\n\nAvailable values:\n- **free**: Free tier with limited credits\n- **lite**: Lite plan with moderate credits\n- **plus**: Plus plan with higher credits\n- **custom**: Custom enterprise plan" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "TTS model version to use for speech synthesis. Different models offer varying capabilities and quality levels.\n\nAvailable models:\n- **ssfm-v30**: Latest model with improved prosody and additional emotion presets (recommended)\n- **ssfm-v21**: Stable production model with proven reliability and consistent quality\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS model version (e.g., ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "List of supported emotions for this model" } }, "description": "Model information including version and supported emotions" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "English voice name." }, "kor": { "type": "string", "title": "Kor", "description": "Korean voice name." } }, "description": "Localized voice names keyed by ISO 639-3 language code." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "Voice type classification.\n\n- `original` — Typecast-provided stock voices available to every account.\n- `custom` — Voices the user created by uploading or cloning their own sample." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "Gender classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **male**: Male voice\n- **female**: Female voice\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "Available emotion presets for speech synthesis. Each emotion affects the tone, pace, and expressiveness of the generated speech.\n\n**ssfm-v21 Supported Emotions (4 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n\n**ssfm-v30 Supported Emotions (7 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n- whisper: Soft, quiet speech\n- toneup: Higher tonal emphasis\n- tonedown: Lower tonal emphasis\n\nCheck available emotions for each voice through the /v2/voices API response.\n" }, "SmartPrompt": { "type": "object", "title": "SmartPrompt (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "Text that comes AFTER the `text` field in TTSRequest. Provides forward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model anticipate emotional transitions\r\n- Leave empty if no following context is available\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "Discriminator field to identify the prompt type. Must be set to \"smart\" for context-aware emotion inference.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "Text that comes BEFORE the `text` field in TTSRequest. Provides backward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model understand emotional build-up and context\r\n- Leave empty if no preceding context is available\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "PresetPrompt (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "Discriminator field to identify the prompt type. Must be set to \"preset\" for preset-based emotion control.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "Emotion preset to apply to the generated speech.\r\n\r\nSupported emotions: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "Controls the strength of emotional expression in the generated speech.\r\n\r\n- 0.0: Completely neutral, no emotional coloring\r\n- 0.5: Subtle emotional hints\r\n- 1.0: Standard emotional expression (default)\r\n- 1.5: Strong emotional emphasis\r\n- 2.0: Maximum intensity, highly expressive\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "Voice use case categories for content type filtering. Each voice is tagged with one or more use cases indicating its suitability for specific content types.\n\n**Available Use Cases:**\n- **Announcer**: Public announcements and presentations\n- **Anime**: Animation and character voices\n- **Audiobook**: Long-form narration and storytelling\n- **Conversational**: Chatbots and conversational AI\n- **Documentary**: Documentary narration and commentary\n- **E-learning**: Educational content and tutorials\n- **Rapper**: Rap and music performance\n- **Game**: Video game characters and narration\n- **Tiktok/Reels**: Short-form social media content\n- **News**: News broadcasting\n- **Podcast**: Broadcasting and podcast production\n- **Voicemail**: IVR systems and voice assistants\n- **Ads**: Advertising and promotional content\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "Error message describing the issue" }, "message": { "type": "string", "description": "Human-readable message for structured errors" }, "error_code": { "type": "string", "description": "Machine-readable error code for structured errors" } }, "description": "API errors use `detail` for legacy and validation errors, or `error_code` and `message` for structured errors." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "Failure reason when `status` is `failed`." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "source": { "type": "string", "title": "Source", "description": "Creation method (`instant` or `professional`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "Creation time in UTC." } }, "description": "A custom voice owned by the authenticated account." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "Recommendation relevance score. Higher values indicate a stronger match for the query." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "Typecast voice identifier with the `tc_` prefix. Use this value as `voice_id` in text-to-speech requests." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "Human-readable voice name." } }, "description": "Recommended voice candidate returned by `GET /v1/voices/recommendations`, sorted by relevance." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "Current creation or training status of a custom voice." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Human-readable voice name (1-30 characters)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "Engine model the voice was cloned for (`ssfm-v21` or `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "Custom voice identifier with the `uc_` prefix. Use this value as `voice_id` in `POST /v1/text-to-speech` and other endpoints that accept `voice_id`." } }, "description": "Response of `POST /v1/voices/clone` — custom voice metadata returned by instant cloning." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "Segment discriminator. Always `pause`." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "Pause duration in seconds. Each pause may be up to 10 seconds; all pauses combined may be up to 60 seconds.", "exclusiveMinimum": 0 } }, "description": "A silent interval inserted without consuming credits.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single word-level alignment segment between the original transcript and the generated audio." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "Current subscription plan" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "Usage limit information" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "Credit usage information" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "Subscription information response model" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation and whitespace)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single character-level alignment segment between the original transcript and the generated audio." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." } }, "description": "Result returned after creating an instant or professional custom voice." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "Base64-encoded audio bytes. Decode and write to a file using the `audio_format` extension." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "Word-level timestamps (with attached punctuation). `null` when the request uses `granularity=char`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "Character-level timestamps (including punctuation and whitespace). `null` when the request uses `granularity=word`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "Audio encoding format of the bytes in `audio` — either `wav` or `mp3`, mirroring the request's `output.audio_format`." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "Length of the generated audio in seconds." } }, "description": "Response payload for POST /v1/text-to-speech/with-timestamps — base64-encoded audio plus per-word and per-character timestamps aligned with the generated speech." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "Audio sample. WAV or MP3, max 25 MB, 5-150 seconds.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "Voice name (1-30 characters)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "Engine model the voice is cloned for (`ssfm-v21` or `ssfm-v30`)." } }, "description": "Multipart request body for instant cloning." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "One WAV or MP3 recording, 25 MiB or less and 5 to 150 seconds long.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "Product Narrator", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." } }, "description": "Multipart request body for instant voice cloning." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "Custom Voice Name", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "Files", "description": "WAV or MP3 recordings used for training. Only one file can be uploaded.\r\n\r\n**Upload limits**\r\n\r\n* One WAV or MP3 file\r\n* File size: 1 GiB or less\r\n* Duration: 5 minutes to 3 hours\r\n* Sample rate: 16 kHz or higher\r\n\r\nFor best results, we recommend audio that meets the following conditions:\r\n\r\n* Record in a speaking style that closely matches how you want the generated voice to sound.\r\n* Record in a quiet environment without background noise.\r\n* Include only one speaker.\r\n* Record in the language specified in the `language` field.\r\n* Longer input audio results in higher-quality generated voices." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." }, "language": { "type": "string", "title": "Language", "example": "eng", "description": "ISO 639-3 language code, such as `kor` or `eng`." } }, "description": "Multipart request body for professional voice cloning." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "API key for authentication. You can obtain an API key from the Typecast API Console." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Compose Text To Speech > Generate multiple speech segments and pauses as one audio file. Add `tts` and `pause` objects to `segments` in the order they should appear. Each `tts` segment accepts the same voice, model, prompt, and output settings as `POST /v1/text-to-speech`; voices and models may differ between segments. **Limits** - Up to 50 total segments, with at least one `tts` segment - Up to 2,000 characters across all `tts` segments - Up to 10 seconds per pause and 60 seconds across all pauses - All `tts` segments must use the same `audio_format` Credits are charged only for the combined text length; pauses are free. Segments are synthesized in parallel and returned in input order. If any segment fails, the entire request fails and no credits are charged. **Response** A successful request returns the composed audio directly as binary data, not JSON. The response `Content-Type` is `audio/wav` or `audio/mpeg`, based on the `audio_format` requested by the segments. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "Production server" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/text-to-speech/compose": { "post": { "tags": [ "Text-to-Speech" ], "x-mint": { "href": "/api-reference/text-to-speech/compose-text-to-speech" }, "summary": "Compose Text To Speech", "security": [ { "ApiKeyAuth": [] } ], "responses": { "200": { "content": { "audio/wav": { "schema": { "type": "string", "format": "binary" }, "example": "[Binary audio data - WAV file content]" } }, "description": "Binary composed audio. The media type matches the requested `audio_format`." }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Unauthorized - Authentication failed" }, "402": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Insufficient credit" } } }, "description": "Insufficient credits for the combined text length" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" } } }, "description": "Invalid segments, compose limits exceeded, or input text cannot be synthesized" }, "500": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "An unexpected error occurred" } } }, "description": "Unexpected server error while synthesizing one or more segments" } }, "deprecated": false, "description": "Generate multiple speech segments and pauses as one audio file. Add `tts` and `pause` objects to `segments` in the order they should appear. Each `tts` segment accepts the same voice, model, prompt, and output settings as `POST /v1/text-to-speech`; voices and models may differ between segments.\n\n**Limits**\n- Up to 50 total segments, with at least one `tts` segment\n- Up to 2,000 characters across all `tts` segments\n- Up to 10 seconds per pause and 60 seconds across all pauses\n- All `tts` segments must use the same `audio_format`\n\nCredits are charged only for the combined text length; pauses are free. Segments are synthesized in parallel and returned in input order. If any segment fails, the entire request fails and no credits are charged.\n\n**Response**\nA successful request returns the composed audio directly as binary data, not JSON. The response `Content-Type` is `audio/wav` or `audio/mpeg`, based on the `audio_format` requested by the segments.", "operationId": "text_to_speech_compose_v1_text_to_speech_compose_post", "requestBody": { "content": { "application/json": { "schema": { "type": "object", "title": "ComposeRequest", "required": [ "segments" ], "properties": { "segments": { "type": "array", "items": { "oneOf": [ { "type": "object", "title": "TTSComposeSegment", "examples": [ { "text": "Welcome to today's update.", "type": "tts", "model": "ssfm-v30", "output": { "audio_format": "wav" }, "language": "eng", "voice_id": "tc_672c5f5ce59fac2a48faeaee" } ], "required": [ "type", "voice_id", "text", "model" ], "properties": { "seed": { "anyOf": [ { "type": "integer", "maximum": 4294967295, "minimum": 0 }, { "type": "null" } ], "title": "Seed", "example": 42, "description": "Optional unsigned integer seed for reproducible synthesis." }, "text": { "type": "string", "title": "Text", "example": "Welcome to today's update.", "maxLength": 2000, "minLength": 1, "description": "Text to synthesize. The combined text across all `tts` segments may not exceed 2,000 characters." }, "type": { "type": "string", "const": "tts", "title": "Type", "default": "tts", "description": "Segment discriminator. Always `tts`." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "Voice model used for this segment. Models may differ between segments." }, "output": { "type": "object", "title": "Output", "properties": { "volume": { "anyOf": [ { "type": "integer", "maximum": 200, "minimum": 0 }, { "type": "null" } ], "title": "Volume", "example": 100, "description": "Adjusts the relative volume of the output audio: 0 (completely silent), 50 (half volume), 100 (standard volume, default), 150 (50% louder than standard), 200 (maximum volume, twice as loud as standard).\r\n\r\nSince this only scales the existing volume, using `volume` can amplify the loudness differences between voices if they have different baseline levels. For consistent output across all clips, use `target_lufs` instead.\r\n\r\n- **Note:** This parameter cannot be used simultaneously with the `target_lufs` parameter.\r\n\r\nRequired range: 0 <= x <= 200\r\n" }, "audio_pitch": { "type": "integer", "title": "Audio Pitch", "default": 0, "example": 0, "maximum": 12, "minimum": -12, "description": "Adjusts the pitch in semitones to affect perceived gender and age: -12 (one octave lower, deeper voice), -6 (half octave lower), 0 (original pitch, default), +6 (half octave higher), +12 (one octave higher, higher voice)" }, "audio_tempo": { "type": "number", "title": "Audio Tempo", "default": 1, "example": 1, "maximum": 2, "minimum": 0.5, "description": "Controls speech speed: 0.5 (half speed, very slow and clear), 0.75 (slightly slower than normal), 1.0 (normal speaking speed, default), 1.5 (50% faster than normal), 2.0 (double speed, very fast speech)" }, "target_lufs": { "type": "number", "anyOf": [ { "type": "number", "maximum": 0, "minimum": -70 }, { "type": "null" } ], "title": "Target Lufs", "example": -14, "description": "Sets the target absolute loudness (LUFS) for the output audio. This normalizes all generated voices to a consistent volume level, regardless of the original source's loudness. Values closer to 0 are louder, while values closer to -70 are quieter.\r\n\r\n- Required range: -70 <= x <= 0\r\n- Recommended values: -14 (common streaming standard), -23 (broadcast standard)\r\n- **Note:** This parameter cannot be used simultaneously with the `volume` parameter. Use `target_lufs` for consistent absolute loudness across different clips, or use `volume` for traditional relative scaling.\r\n" }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "default": "wav", "example": "wav", "description": "Output audio format.\r\n\r\n**WAV format:**\r\n- Uncompressed PCM audio\r\n- 16-bit depth, mono channel, 44100 Hz sample rate\r\n- Higher quality, larger file size\r\n- Recommended for professional audio production\r\n\r\n**MP3 format:**\r\n- Compressed MPEG Layer III audio\r\n- 320 kbps bitrate, 44100 Hz sample rate\r\n- Smaller file size\r\n- Recommended for web streaming and distribution\r\n" }, "remove_silence_ms": { "anyOf": [ { "type": "integer", "maximum": 1000, "minimum": 0 }, { "type": "null" } ], "title": "Remove Silence Ms", "default": null, "example": 100, "description": "When enabled, shortens detected silences longer than the specified duration to that duration. The value is in milliseconds (ms). The value is the duration of silence to retain, not the amount to remove.\r\n\r\n**Accepted values:**\r\n\r\n- An integer from **0 to 1000**, with a recommended range of 0 to 200.\r\n- Omitted or `null`: silence removal is disabled.\r\n- `0`: removes detected silence longer than 0 ms. **This does not disable the feature.**\r\n- Booleans, strings, fractional values, and out-of-range values are invalid.\r\n\r\n**Example:** `100` shortens detected silences longer than 100 ms to 100 ms. Lower values include shorter silence segments for removal and leave less silence in each affected segment." } }, "description": "Audio settings for this segment. All segments must use the same `audio_format`.\r\n\r\nUse `remove_silence_ms` (integer, 0–1000 ms) to shorten detected silence." }, "prompt": { "oneOf": [ { "$ref": "#/components/schemas/SmartPrompt" }, { "$ref": "#/components/schemas/PresetPrompt" }, { "$ref": "#/components/schemas/Prompt" } ], "title": "Prompt", "description": "Emotion and context settings for this segment." }, "language": { "type": "string", "title": "Language", "example": "eng", "description": "ISO 639-3 language code. If omitted, the language is detected from the text." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_672c5f5ce59fac2a48faeaee", "description": "Built-in (`tc_`) or custom (`uc_`) Typecast voice identifier." } }, "description": "A speech segment with the same synthesis options as a standard text-to-speech request." }, { "$ref": "#/components/schemas/PauseComposeSegment" } ], "discriminator": { "mapping": { "tts": "#/components/schemas/TTSComposeSegment", "pause": "#/components/schemas/PauseComposeSegment" }, "propertyName": "type" } }, "title": "Segments", "maxItems": 50, "minItems": 1, "description": "Speech and pause segments in output order. Provide 1–50 segments with at least one `tts` segment." } }, "description": "A sequence of speech and pause segments returned as one audio file." }, "example": { "segments": [ { "text": "Welcome to today's update.", "type": "tts", "model": "ssfm-v30", "output": { "audio_format": "wav" }, "language": "eng", "voice_id": "tc_672c5f5ce59fac2a48faeaee" }, { "type": "pause", "duration_seconds": 1.5 }, { "text": "Here is the first story.", "type": "tts", "model": "ssfm-v30", "output": { "audio_format": "wav" }, "language": "eng", "voice_id": "tc_66aca22c7d31e45ff05ff418" } ] } } }, "required": true, "description": "" }, "x-codeSamples": [ { "lang": "cURL", "label": "cURL (save to file)", "source": "curl --request POST \\\n --url https://api.typecast.ai/v1/text-to-speech/compose \\\n --header 'Content-Type: application/json' \\\n --header 'X-API-KEY: ' \\\n --output output.wav \\\n --data @- <\"},\n json={\n \"segments\": [\n {\n \"type\": \"tts\",\n \"voice_id\": \"tc_672c5f5ce59fac2a48faeaee\",\n \"text\": \"Welcome to today's update.\",\n \"model\": \"ssfm-v30\",\n \"language\": \"eng\",\n \"output\": {\"audio_format\": \"wav\"},\n },\n {\"type\": \"pause\", \"duration_seconds\": 1.5},\n {\n \"type\": \"tts\",\n \"voice_id\": \"tc_66aca22c7d31e45ff05ff418\",\n \"text\": \"Here is the first story.\",\n \"model\": \"ssfm-v30\",\n \"language\": \"eng\",\n \"output\": {\"audio_format\": \"wav\"},\n },\n ]\n },\n timeout=120,\n)\nresponse.raise_for_status()\nwith open(\"output.wav\", \"wb\") as audio_file:\n audio_file.write(response.content)\n" }, { "lang": "cURL", "label": "Silence removal", "source": "curl --request POST 'https://api.typecast.ai/v1/text-to-speech/compose' \\\n --header 'X-API-KEY: ' \\\n --header 'Content-Type: application/json' \\\n --output review.wav \\\n --data-binary @- <<'JSON'\n{\n \"segments\": [\n {\n \"type\": \"tts\",\n \"voice_id\": \"\",\n \"text\": \"Hello. Thank you for listening.\",\n \"model\": \"ssfm-v30\",\n \"output\": {\n \"audio_format\": \"wav\",\n \"remove_silence_ms\": 300\n }\n },\n {\n \"type\": \"pause\",\n \"duration_seconds\": 1.5\n },\n {\n \"type\": \"tts\",\n \"voice_id\": \"\",\n \"text\": \"Hello. Thank you for listening.\",\n \"model\": \"ssfm-v30\",\n \"output\": {\n \"audio_format\": \"wav\",\n \"remove_silence_ms\": 300\n }\n }\n ]\n}\nJSON\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "Maximum number of concurrent requests allowed" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "Maximum number of custom voices (created via instant cloning) allowed on the current plan." }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "Usage limit information" }, "Prompt": { "title": "Prompt (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "Emotion preset to apply.\r\n\r\nSupported emotions for ssfm-v21: normal, happy, sad, angry\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "example": 1, "description": "Controls the strength of emotional expression (0.0 to 2.0).\r\n\r\n- 0.0: Completely neutral\r\n- 1.0: Standard expression (default)\r\n- 2.0: Maximum intensity\r\n" } }, "description": "Emotion and style settings for the generated speech." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "Age group classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **child**: Child voice (under 12 years old)\n- **teenager**: Teenage voice (13-19 years old)\n- **young_adult**: Young adult voice (20-35 years old)\n- **middle_age**: Middle-aged voice (36-60 years old)\n- **elder**: Elder voice (over 60 years old)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "Total monthly credits provided by the plan" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "Number of credits used" } }, "description": "Credit usage information" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group classification (child/teenager/young_adult/middle_age/elder)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender classification (male/female)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "List of supported TTS models with their available emotions (e.g., [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix (e.g., `tc_60e5426de8b95f1d3000d7b5`); cloned custom voices created via `POST /v1/voices/clone` use the `uc_` prefix and are also returned by `/v2/voices` for the owner." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "List of use case categories this voice is suitable for" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "Human-readable name of the voice" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type — `original` for Typecast-provided stock voices, `custom` for user-cloned voices." } }, "description": "V2 Voice response model with model-grouped emotions and enhanced metadata" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender (`male` or `female`)." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "Supported TTS models and their available emotions." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix and custom voices use the `uc_` prefix." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "Recommended use-case categories." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "Localized voice names, such as `{\"eng\": \"Daejin\", \"kor\": \"대진\"}`." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type (`original` or `custom`)." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "Preview audio URL when available." } }, "description": "Voice metadata with localized names and a preview audio URL." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "Subscription plan for the API.\n\nAvailable values:\n- **free**: Free tier with limited credits\n- **lite**: Lite plan with moderate credits\n- **plus**: Plus plan with higher credits\n- **custom**: Custom enterprise plan" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "TTS model version to use for speech synthesis. Different models offer varying capabilities and quality levels.\n\nAvailable models:\n- **ssfm-v30**: Latest model with improved prosody and additional emotion presets (recommended)\n- **ssfm-v21**: Stable production model with proven reliability and consistent quality\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS model version (e.g., ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "List of supported emotions for this model" } }, "description": "Model information including version and supported emotions" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "English voice name." }, "kor": { "type": "string", "title": "Kor", "description": "Korean voice name." } }, "description": "Localized voice names keyed by ISO 639-3 language code." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "Voice type classification.\n\n- `original` — Typecast-provided stock voices available to every account.\n- `custom` — Voices the user created by uploading or cloning their own sample." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "Gender classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **male**: Male voice\n- **female**: Female voice\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "Available emotion presets for speech synthesis. Each emotion affects the tone, pace, and expressiveness of the generated speech.\n\n**ssfm-v21 Supported Emotions (4 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n\n**ssfm-v30 Supported Emotions (7 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n- whisper: Soft, quiet speech\n- toneup: Higher tonal emphasis\n- tonedown: Lower tonal emphasis\n\nCheck available emotions for each voice through the /v2/voices API response.\n" }, "SmartPrompt": { "type": "object", "title": "SmartPrompt (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "Text that comes AFTER the `text` field in TTSRequest. Provides forward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model anticipate emotional transitions\r\n- Leave empty if no following context is available\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "Discriminator field to identify the prompt type. Must be set to \"smart\" for context-aware emotion inference.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "Text that comes BEFORE the `text` field in TTSRequest. Provides backward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model understand emotional build-up and context\r\n- Leave empty if no preceding context is available\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "PresetPrompt (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "Discriminator field to identify the prompt type. Must be set to \"preset\" for preset-based emotion control.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "Emotion preset to apply to the generated speech.\r\n\r\nSupported emotions: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "Controls the strength of emotional expression in the generated speech.\r\n\r\n- 0.0: Completely neutral, no emotional coloring\r\n- 0.5: Subtle emotional hints\r\n- 1.0: Standard emotional expression (default)\r\n- 1.5: Strong emotional emphasis\r\n- 2.0: Maximum intensity, highly expressive\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "Voice use case categories for content type filtering. Each voice is tagged with one or more use cases indicating its suitability for specific content types.\n\n**Available Use Cases:**\n- **Announcer**: Public announcements and presentations\n- **Anime**: Animation and character voices\n- **Audiobook**: Long-form narration and storytelling\n- **Conversational**: Chatbots and conversational AI\n- **Documentary**: Documentary narration and commentary\n- **E-learning**: Educational content and tutorials\n- **Rapper**: Rap and music performance\n- **Game**: Video game characters and narration\n- **Tiktok/Reels**: Short-form social media content\n- **News**: News broadcasting\n- **Podcast**: Broadcasting and podcast production\n- **Voicemail**: IVR systems and voice assistants\n- **Ads**: Advertising and promotional content\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "Error message describing the issue" }, "message": { "type": "string", "description": "Human-readable message for structured errors" }, "error_code": { "type": "string", "description": "Machine-readable error code for structured errors" } }, "description": "API errors use `detail` for legacy and validation errors, or `error_code` and `message` for structured errors." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "Failure reason when `status` is `failed`." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "source": { "type": "string", "title": "Source", "description": "Creation method (`instant` or `professional`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "Creation time in UTC." } }, "description": "A custom voice owned by the authenticated account." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "Recommendation relevance score. Higher values indicate a stronger match for the query." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "Typecast voice identifier with the `tc_` prefix. Use this value as `voice_id` in text-to-speech requests." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "Human-readable voice name." } }, "description": "Recommended voice candidate returned by `GET /v1/voices/recommendations`, sorted by relevance." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "Current creation or training status of a custom voice." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Human-readable voice name (1-30 characters)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "Engine model the voice was cloned for (`ssfm-v21` or `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "Custom voice identifier with the `uc_` prefix. Use this value as `voice_id` in `POST /v1/text-to-speech` and other endpoints that accept `voice_id`." } }, "description": "Response of `POST /v1/voices/clone` — custom voice metadata returned by instant cloning." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "Segment discriminator. Always `pause`." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "Pause duration in seconds. Each pause may be up to 10 seconds; all pauses combined may be up to 60 seconds.", "exclusiveMinimum": 0 } }, "description": "A silent interval inserted without consuming credits.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single word-level alignment segment between the original transcript and the generated audio." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "Current subscription plan" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "Usage limit information" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "Credit usage information" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "Subscription information response model" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation and whitespace)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single character-level alignment segment between the original transcript and the generated audio." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." } }, "description": "Result returned after creating an instant or professional custom voice." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "Base64-encoded audio bytes. Decode and write to a file using the `audio_format` extension." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "Word-level timestamps (with attached punctuation). `null` when the request uses `granularity=char`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "Character-level timestamps (including punctuation and whitespace). `null` when the request uses `granularity=word`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "Audio encoding format of the bytes in `audio` — either `wav` or `mp3`, mirroring the request's `output.audio_format`." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "Length of the generated audio in seconds." } }, "description": "Response payload for POST /v1/text-to-speech/with-timestamps — base64-encoded audio plus per-word and per-character timestamps aligned with the generated speech." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "Audio sample. WAV or MP3, max 25 MB, 5-150 seconds.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "Voice name (1-30 characters)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "Engine model the voice is cloned for (`ssfm-v21` or `ssfm-v30`)." } }, "description": "Multipart request body for instant cloning." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "One WAV or MP3 recording, 25 MiB or less and 5 to 150 seconds long.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "Product Narrator", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." } }, "description": "Multipart request body for instant voice cloning." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "Custom Voice Name", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "Files", "description": "WAV or MP3 recordings used for training. Only one file can be uploaded.\r\n\r\n**Upload limits**\r\n\r\n* One WAV or MP3 file\r\n* File size: 1 GiB or less\r\n* Duration: 5 minutes to 3 hours\r\n* Sample rate: 16 kHz or higher\r\n\r\nFor best results, we recommend audio that meets the following conditions:\r\n\r\n* Record in a speaking style that closely matches how you want the generated voice to sound.\r\n* Record in a quiet environment without background noise.\r\n* Include only one speaker.\r\n* Record in the language specified in the `language` field.\r\n* Longer input audio results in higher-quality generated voices." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." }, "language": { "type": "string", "title": "Language", "example": "eng", "description": "ISO 639-3 language code, such as `kor` or `eng`." } }, "description": "Multipart request body for professional voice cloning." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "API key for authentication. You can obtain an API key from the Typecast API Console." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # List Voices > Lists the built-in voices and completed custom voices available for use with any endpoint that accepts a `voice_id`. Custom voices appear first. Each `voice_name` is keyed by ISO 639-3 language codes such as `eng` and `kor`. Use the optional filters together to narrow the catalog by model, gender, age group, use case, or voice type. `preview_url` is `null` when a preview is unavailable, including custom voices. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "Production server" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v3/voices": { "get": { "tags": [ "Voices" ], "x-mint": { "href": "/api-reference/voices/list-voices" }, "summary": "List Voices", "security": [ { "ApiKeyAuth": [] } ], "responses": {}, "parameters": [ { "in": "query", "name": "model", "schema": { "$ref": "#/components/schemas/TTSModel", "description": "Filter by TTS model, such as `ssfm-v21`, `ssfm-v30`, or `ssfm-v31`." }, "required": false, "description": "Filter by TTS model, such as `ssfm-v21`, `ssfm-v30`, or `ssfm-v31`." }, { "in": "query", "name": "gender", "schema": { "$ref": "#/components/schemas/GenderEnum", "description": "Filter by voice gender (`male` or `female`)." }, "required": false, "description": "Filter by voice gender (`male` or `female`)." }, { "in": "query", "name": "age", "schema": { "$ref": "#/components/schemas/AgeEnum", "description": "Filter by age group (`child`, `teenager`, `young_adult`, `middle_age`, or `elder`)." }, "required": false, "description": "Filter by age group (`child`, `teenager`, `young_adult`, `middle_age`, or `elder`)." }, { "in": "query", "name": "use_cases", "schema": { "type": "string", "title": "Use Cases", "description": "Filter by a use-case keyword, such as `Ads`." }, "required": false, "description": "Filter by a use-case keyword, such as `Ads`." }, { "in": "query", "name": "voice_type", "schema": { "$ref": "#/components/schemas/VoiceType", "description": "Filter by voice type (`original` or `custom`)." }, "required": false, "description": "Filter by voice type (`original` or `custom`)." } ], "description": "Lists the built-in voices and completed custom voices available for use with any endpoint that accepts a `voice_id`. Custom voices appear first. Each `voice_name` is keyed by ISO 639-3 language codes such as `eng` and `kor`.\r\n\r\nUse the optional filters together to narrow the catalog by model, gender, age group, use case, or voice type. `preview_url` is `null` when a preview is unavailable, including custom voices.", "operationId": "get_voices_v3_v3_voices_get", "x-codeSamples": [] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "Maximum number of concurrent requests allowed" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "Maximum number of custom voices (created via instant cloning) allowed on the current plan." }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "Usage limit information" }, "Prompt": { "title": "Prompt (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "Emotion preset to apply.\r\n\r\nSupported emotions for ssfm-v21: normal, happy, sad, angry\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "example": 1, "description": "Controls the strength of emotional expression (0.0 to 2.0).\r\n\r\n- 0.0: Completely neutral\r\n- 1.0: Standard expression (default)\r\n- 2.0: Maximum intensity\r\n" } }, "description": "Emotion and style settings for the generated speech." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "Age group classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **child**: Child voice (under 12 years old)\n- **teenager**: Teenage voice (13-19 years old)\n- **young_adult**: Young adult voice (20-35 years old)\n- **middle_age**: Middle-aged voice (36-60 years old)\n- **elder**: Elder voice (over 60 years old)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "Total monthly credits provided by the plan" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "Number of credits used" } }, "description": "Credit usage information" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group classification (child/teenager/young_adult/middle_age/elder)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender classification (male/female)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "List of supported TTS models with their available emotions (e.g., [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix (e.g., `tc_60e5426de8b95f1d3000d7b5`); cloned custom voices created via `POST /v1/voices/clone` use the `uc_` prefix and are also returned by `/v2/voices` for the owner." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "List of use case categories this voice is suitable for" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "Human-readable name of the voice" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type — `original` for Typecast-provided stock voices, `custom` for user-cloned voices." } }, "description": "V2 Voice response model with model-grouped emotions and enhanced metadata" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender (`male` or `female`)." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "Supported TTS models and their available emotions." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix and custom voices use the `uc_` prefix." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "Recommended use-case categories." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "Localized voice names, such as `{\"eng\": \"Daejin\", \"kor\": \"대진\"}`." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type (`original` or `custom`)." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "Preview audio URL when available." } }, "description": "Voice metadata with localized names and a preview audio URL." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "Subscription plan for the API.\n\nAvailable values:\n- **free**: Free tier with limited credits\n- **lite**: Lite plan with moderate credits\n- **plus**: Plus plan with higher credits\n- **custom**: Custom enterprise plan" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "TTS model version to use for speech synthesis. Different models offer varying capabilities and quality levels.\n\nAvailable models:\n- **ssfm-v30**: Latest model with improved prosody and additional emotion presets (recommended)\n- **ssfm-v21**: Stable production model with proven reliability and consistent quality\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS model version (e.g., ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "List of supported emotions for this model" } }, "description": "Model information including version and supported emotions" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "English voice name." }, "kor": { "type": "string", "title": "Kor", "description": "Korean voice name." } }, "description": "Localized voice names keyed by ISO 639-3 language code." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "Voice type classification.\n\n- `original` — Typecast-provided stock voices available to every account.\n- `custom` — Voices the user created by uploading or cloning their own sample." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "Gender classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **male**: Male voice\n- **female**: Female voice\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "Available emotion presets for speech synthesis. Each emotion affects the tone, pace, and expressiveness of the generated speech.\n\n**ssfm-v21 Supported Emotions (4 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n\n**ssfm-v30 Supported Emotions (7 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n- whisper: Soft, quiet speech\n- toneup: Higher tonal emphasis\n- tonedown: Lower tonal emphasis\n\nCheck available emotions for each voice through the /v2/voices API response.\n" }, "SmartPrompt": { "type": "object", "title": "SmartPrompt (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "Text that comes AFTER the `text` field in TTSRequest. Provides forward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model anticipate emotional transitions\r\n- Leave empty if no following context is available\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "Discriminator field to identify the prompt type. Must be set to \"smart\" for context-aware emotion inference.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "Text that comes BEFORE the `text` field in TTSRequest. Provides backward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model understand emotional build-up and context\r\n- Leave empty if no preceding context is available\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "PresetPrompt (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "Discriminator field to identify the prompt type. Must be set to \"preset\" for preset-based emotion control.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "Emotion preset to apply to the generated speech.\r\n\r\nSupported emotions: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "Controls the strength of emotional expression in the generated speech.\r\n\r\n- 0.0: Completely neutral, no emotional coloring\r\n- 0.5: Subtle emotional hints\r\n- 1.0: Standard emotional expression (default)\r\n- 1.5: Strong emotional emphasis\r\n- 2.0: Maximum intensity, highly expressive\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "Voice use case categories for content type filtering. Each voice is tagged with one or more use cases indicating its suitability for specific content types.\n\n**Available Use Cases:**\n- **Announcer**: Public announcements and presentations\n- **Anime**: Animation and character voices\n- **Audiobook**: Long-form narration and storytelling\n- **Conversational**: Chatbots and conversational AI\n- **Documentary**: Documentary narration and commentary\n- **E-learning**: Educational content and tutorials\n- **Rapper**: Rap and music performance\n- **Game**: Video game characters and narration\n- **Tiktok/Reels**: Short-form social media content\n- **News**: News broadcasting\n- **Podcast**: Broadcasting and podcast production\n- **Voicemail**: IVR systems and voice assistants\n- **Ads**: Advertising and promotional content\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "Error message describing the issue" }, "message": { "type": "string", "description": "Human-readable message for structured errors" }, "error_code": { "type": "string", "description": "Machine-readable error code for structured errors" } }, "description": "API errors use `detail` for legacy and validation errors, or `error_code` and `message` for structured errors." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "Failure reason when `status` is `failed`." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "source": { "type": "string", "title": "Source", "description": "Creation method (`instant` or `professional`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "Creation time in UTC." } }, "description": "A custom voice owned by the authenticated account." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "Recommendation relevance score. Higher values indicate a stronger match for the query." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "Typecast voice identifier with the `tc_` prefix. Use this value as `voice_id` in text-to-speech requests." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "Human-readable voice name." } }, "description": "Recommended voice candidate returned by `GET /v1/voices/recommendations`, sorted by relevance." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "Current creation or training status of a custom voice." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Human-readable voice name (1-30 characters)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "Engine model the voice was cloned for (`ssfm-v21` or `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "Custom voice identifier with the `uc_` prefix. Use this value as `voice_id` in `POST /v1/text-to-speech` and other endpoints that accept `voice_id`." } }, "description": "Response of `POST /v1/voices/clone` — custom voice metadata returned by instant cloning." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "Segment discriminator. Always `pause`." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "Pause duration in seconds. Each pause may be up to 10 seconds; all pauses combined may be up to 60 seconds.", "exclusiveMinimum": 0 } }, "description": "A silent interval inserted without consuming credits.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single word-level alignment segment between the original transcript and the generated audio." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "Current subscription plan" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "Usage limit information" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "Credit usage information" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "Subscription information response model" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation and whitespace)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single character-level alignment segment between the original transcript and the generated audio." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." } }, "description": "Result returned after creating an instant or professional custom voice." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "Base64-encoded audio bytes. Decode and write to a file using the `audio_format` extension." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "Word-level timestamps (with attached punctuation). `null` when the request uses `granularity=char`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "Character-level timestamps (including punctuation and whitespace). `null` when the request uses `granularity=word`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "Audio encoding format of the bytes in `audio` — either `wav` or `mp3`, mirroring the request's `output.audio_format`." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "Length of the generated audio in seconds." } }, "description": "Response payload for POST /v1/text-to-speech/with-timestamps — base64-encoded audio plus per-word and per-character timestamps aligned with the generated speech." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "Audio sample. WAV or MP3, max 25 MB, 5-150 seconds.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "Voice name (1-30 characters)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "Engine model the voice is cloned for (`ssfm-v21` or `ssfm-v30`)." } }, "description": "Multipart request body for instant cloning." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "One WAV or MP3 recording, 25 MiB or less and 5 to 150 seconds long.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "Product Narrator", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." } }, "description": "Multipart request body for instant voice cloning." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "Custom Voice Name", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "Files", "description": "WAV or MP3 recordings used for training. Only one file can be uploaded.\r\n\r\n**Upload limits**\r\n\r\n* One WAV or MP3 file\r\n* File size: 1 GiB or less\r\n* Duration: 5 minutes to 3 hours\r\n* Sample rate: 16 kHz or higher\r\n\r\nFor best results, we recommend audio that meets the following conditions:\r\n\r\n* Record in a speaking style that closely matches how you want the generated voice to sound.\r\n* Record in a quiet environment without background noise.\r\n* Include only one speaker.\r\n* Record in the language specified in the `language` field.\r\n* Longer input audio results in higher-quality generated voices." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." }, "language": { "type": "string", "title": "Language", "example": "eng", "description": "ISO 639-3 language code, such as `kor` or `eng`." } }, "description": "Multipart request body for professional voice cloning." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "API key for authentication. You can obtain an API key from the Typecast API Console." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Get Voice Details > Returns one accessible built-in or completed custom voice with localized names, supported models and emotions, recommended use cases, and a preview URL when available. Built-in voice IDs use the `tc_` prefix; custom voice IDs use `uc_`. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "Production server" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v3/voices/{voice_id}": { "get": { "tags": [ "Voices" ], "x-mint": { "href": "/api-reference/voices/get-voice-details" }, "summary": "Get Voice Details", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VoiceV3" }, "example": { "age": "young_adult", "gender": "female", "models": [ { "version": "ssfm-v30", "emotions": [ "normal", "happy", "sad", "angry" ] } ], "voice_id": "tc_6045d56d5f9ae03ac175cf73", "use_cases": [ "Game", "Anime" ], "voice_name": { "eng": "Valkyrie", "kor": "발키리" }, "voice_type": "original", "preview_url": "https://static2.typecast.ai/data/actor/valkyrie.mp3" } } }, "description": "Voice retrieved successfully" }, "400": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Invalid voice ID", "error_code": "INVALID_VOICE_ID" } } }, "description": "The voice ID format is invalid" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Authentication failed" }, "404": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Voice not found", "error_code": "VOICE_NOT_FOUND" } } }, "description": "The voice does not exist or is not available to this account" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid request format" } } }, "description": "Validation Error - Invalid voice ID" } }, "parameters": [ { "in": "path", "name": "voice_id", "schema": { "type": "string", "title": "Voice Id" }, "required": true, "description": "Unique voice identifier." } ], "description": "Returns one accessible built-in or completed custom voice with localized names, supported models and emotions, recommended use cases, and a preview URL when available. Built-in voice IDs use the `tc_` prefix; custom voice IDs use `uc_`.", "operationId": "get_voice_v3_v3_voices__voice_id__get", "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request GET \\\n --url https://api.typecast.ai/v3/voices/tc_6045d56d5f9ae03ac175cf73 \\\n --header 'X-API-KEY: '\n" }, { "lang": "Python", "label": "Python (requests)", "source": "import requests\n\nresponse = requests.get(\n \"https://api.typecast.ai/v3/voices/tc_6045d56d5f9ae03ac175cf73\",\n headers={\"X-API-KEY\": \"\"},\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "Maximum number of concurrent requests allowed" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "Maximum number of custom voices (created via instant cloning) allowed on the current plan." }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "Usage limit information" }, "Prompt": { "title": "Prompt (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "Emotion preset to apply.\r\n\r\nSupported emotions for ssfm-v21: normal, happy, sad, angry\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "example": 1, "description": "Controls the strength of emotional expression (0.0 to 2.0).\r\n\r\n- 0.0: Completely neutral\r\n- 1.0: Standard expression (default)\r\n- 2.0: Maximum intensity\r\n" } }, "description": "Emotion and style settings for the generated speech." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "Age group classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **child**: Child voice (under 12 years old)\n- **teenager**: Teenage voice (13-19 years old)\n- **young_adult**: Young adult voice (20-35 years old)\n- **middle_age**: Middle-aged voice (36-60 years old)\n- **elder**: Elder voice (over 60 years old)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "Total monthly credits provided by the plan" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "Number of credits used" } }, "description": "Credit usage information" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group classification (child/teenager/young_adult/middle_age/elder)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender classification (male/female)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "List of supported TTS models with their available emotions (e.g., [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix (e.g., `tc_60e5426de8b95f1d3000d7b5`); cloned custom voices created via `POST /v1/voices/clone` use the `uc_` prefix and are also returned by `/v2/voices` for the owner." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "List of use case categories this voice is suitable for" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "Human-readable name of the voice" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type — `original` for Typecast-provided stock voices, `custom` for user-cloned voices." } }, "description": "V2 Voice response model with model-grouped emotions and enhanced metadata" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender (`male` or `female`)." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "Supported TTS models and their available emotions." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix and custom voices use the `uc_` prefix." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "Recommended use-case categories." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "Localized voice names, such as `{\"eng\": \"Daejin\", \"kor\": \"대진\"}`." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type (`original` or `custom`)." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "Preview audio URL when available." } }, "description": "Voice metadata with localized names and a preview audio URL." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "Subscription plan for the API.\n\nAvailable values:\n- **free**: Free tier with limited credits\n- **lite**: Lite plan with moderate credits\n- **plus**: Plus plan with higher credits\n- **custom**: Custom enterprise plan" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "TTS model version to use for speech synthesis. Different models offer varying capabilities and quality levels.\n\nAvailable models:\n- **ssfm-v30**: Latest model with improved prosody and additional emotion presets (recommended)\n- **ssfm-v21**: Stable production model with proven reliability and consistent quality\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS model version (e.g., ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "List of supported emotions for this model" } }, "description": "Model information including version and supported emotions" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "English voice name." }, "kor": { "type": "string", "title": "Kor", "description": "Korean voice name." } }, "description": "Localized voice names keyed by ISO 639-3 language code." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "Voice type classification.\n\n- `original` — Typecast-provided stock voices available to every account.\n- `custom` — Voices the user created by uploading or cloning their own sample." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "Gender classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **male**: Male voice\n- **female**: Female voice\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "Available emotion presets for speech synthesis. Each emotion affects the tone, pace, and expressiveness of the generated speech.\n\n**ssfm-v21 Supported Emotions (4 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n\n**ssfm-v30 Supported Emotions (7 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n- whisper: Soft, quiet speech\n- toneup: Higher tonal emphasis\n- tonedown: Lower tonal emphasis\n\nCheck available emotions for each voice through the /v2/voices API response.\n" }, "SmartPrompt": { "type": "object", "title": "SmartPrompt (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "Text that comes AFTER the `text` field in TTSRequest. Provides forward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model anticipate emotional transitions\r\n- Leave empty if no following context is available\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "Discriminator field to identify the prompt type. Must be set to \"smart\" for context-aware emotion inference.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "Text that comes BEFORE the `text` field in TTSRequest. Provides backward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model understand emotional build-up and context\r\n- Leave empty if no preceding context is available\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "PresetPrompt (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "Discriminator field to identify the prompt type. Must be set to \"preset\" for preset-based emotion control.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "Emotion preset to apply to the generated speech.\r\n\r\nSupported emotions: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "Controls the strength of emotional expression in the generated speech.\r\n\r\n- 0.0: Completely neutral, no emotional coloring\r\n- 0.5: Subtle emotional hints\r\n- 1.0: Standard emotional expression (default)\r\n- 1.5: Strong emotional emphasis\r\n- 2.0: Maximum intensity, highly expressive\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "Voice use case categories for content type filtering. Each voice is tagged with one or more use cases indicating its suitability for specific content types.\n\n**Available Use Cases:**\n- **Announcer**: Public announcements and presentations\n- **Anime**: Animation and character voices\n- **Audiobook**: Long-form narration and storytelling\n- **Conversational**: Chatbots and conversational AI\n- **Documentary**: Documentary narration and commentary\n- **E-learning**: Educational content and tutorials\n- **Rapper**: Rap and music performance\n- **Game**: Video game characters and narration\n- **Tiktok/Reels**: Short-form social media content\n- **News**: News broadcasting\n- **Podcast**: Broadcasting and podcast production\n- **Voicemail**: IVR systems and voice assistants\n- **Ads**: Advertising and promotional content\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "Error message describing the issue" }, "message": { "type": "string", "description": "Human-readable message for structured errors" }, "error_code": { "type": "string", "description": "Machine-readable error code for structured errors" } }, "description": "API errors use `detail` for legacy and validation errors, or `error_code` and `message` for structured errors." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "Failure reason when `status` is `failed`." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "source": { "type": "string", "title": "Source", "description": "Creation method (`instant` or `professional`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "Creation time in UTC." } }, "description": "A custom voice owned by the authenticated account." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "Recommendation relevance score. Higher values indicate a stronger match for the query." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "Typecast voice identifier with the `tc_` prefix. Use this value as `voice_id` in text-to-speech requests." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "Human-readable voice name." } }, "description": "Recommended voice candidate returned by `GET /v1/voices/recommendations`, sorted by relevance." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "Current creation or training status of a custom voice." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Human-readable voice name (1-30 characters)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "Engine model the voice was cloned for (`ssfm-v21` or `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "Custom voice identifier with the `uc_` prefix. Use this value as `voice_id` in `POST /v1/text-to-speech` and other endpoints that accept `voice_id`." } }, "description": "Response of `POST /v1/voices/clone` — custom voice metadata returned by instant cloning." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "Segment discriminator. Always `pause`." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "Pause duration in seconds. Each pause may be up to 10 seconds; all pauses combined may be up to 60 seconds.", "exclusiveMinimum": 0 } }, "description": "A silent interval inserted without consuming credits.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single word-level alignment segment between the original transcript and the generated audio." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "Current subscription plan" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "Usage limit information" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "Credit usage information" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "Subscription information response model" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation and whitespace)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single character-level alignment segment between the original transcript and the generated audio." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." } }, "description": "Result returned after creating an instant or professional custom voice." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "Base64-encoded audio bytes. Decode and write to a file using the `audio_format` extension." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "Word-level timestamps (with attached punctuation). `null` when the request uses `granularity=char`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "Character-level timestamps (including punctuation and whitespace). `null` when the request uses `granularity=word`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "Audio encoding format of the bytes in `audio` — either `wav` or `mp3`, mirroring the request's `output.audio_format`." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "Length of the generated audio in seconds." } }, "description": "Response payload for POST /v1/text-to-speech/with-timestamps — base64-encoded audio plus per-word and per-character timestamps aligned with the generated speech." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "Audio sample. WAV or MP3, max 25 MB, 5-150 seconds.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "Voice name (1-30 characters)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "Engine model the voice is cloned for (`ssfm-v21` or `ssfm-v30`)." } }, "description": "Multipart request body for instant cloning." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "One WAV or MP3 recording, 25 MiB or less and 5 to 150 seconds long.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "Product Narrator", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." } }, "description": "Multipart request body for instant voice cloning." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "Custom Voice Name", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "Files", "description": "WAV or MP3 recordings used for training. Only one file can be uploaded.\r\n\r\n**Upload limits**\r\n\r\n* One WAV or MP3 file\r\n* File size: 1 GiB or less\r\n* Duration: 5 minutes to 3 hours\r\n* Sample rate: 16 kHz or higher\r\n\r\nFor best results, we recommend audio that meets the following conditions:\r\n\r\n* Record in a speaking style that closely matches how you want the generated voice to sound.\r\n* Record in a quiet environment without background noise.\r\n* Include only one speaker.\r\n* Record in the language specified in the `language` field.\r\n* Longer input audio results in higher-quality generated voices." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." }, "language": { "type": "string", "title": "Language", "example": "eng", "description": "ISO 639-3 language code, such as `kor` or `eng`." } }, "description": "Multipart request body for professional voice cloning." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "API key for authentication. You can obtain an API key from the Typecast API Console." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Recommend Voices > This API recommends the best Typecast voices based on text descriptions. Instead of manually searching for a specific `voice_id`, you can find the perfect voice by simply inputting keywords or sentences describing the desired style, mood, language, or use case. The response is sorted by recommendation score, so you can pass the top candidate directly to text-to-speech endpoints such as `POST /v1/text-to-speech`. The response includes only `voice_id`, `voice_name`, and `score`. Use `GET /v2/voices` or `GET /v2/voices/{voice_id}` when you need detailed metadata such as supported models, emotions, gender, age, or use cases for the recommended voices. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "Production server" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/voices/recommendations": { "get": { "tags": [ "Voices" ], "x-mint": { "href": "/api-reference/voices/recommend-voices" }, "summary": "Recommend Voices", "responses": { "200": { "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/RecommendedVoice" }, "title": "Response Recommend Voices V1 Voices Recommendations Get", "maxItems": 10 }, "example": [ { "score": 0.92, "voice_id": "tc_60e5426de8b95f1d3000d7b5", "voice_name": "Olivia" }, { "score": 0.87, "voice_id": "tc_62a8975e695ad26f7fb514d1", "voice_name": "Emma" } ] } }, "description": "Success - Returns recommended voices sorted by score" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Unauthorized - Invalid or missing API key" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" }, "example": { "detail": [ { "loc": [ "query", "query" ], "msg": "String should have at most 500 characters", "type": "string_too_long" } ] } } }, "description": "Validation Error - Invalid request parameters" }, "500": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "An unexpected error occurred" } } }, "description": "Internal Server Error - Voice recommendation failed" } }, "parameters": [ { "in": "query", "name": "query", "schema": { "type": "string", "title": "Query", "maxLength": 500, "minLength": 1, "description": "Text description. Describe the desired style, mood, language, use case, or content context with keywords or sentences." }, "example": "warm female voice for product tutorial", "required": true, "description": "Text description. Describe the desired style, mood, language, use case, or content context with keywords or sentences." }, { "in": "query", "name": "count", "schema": { "type": "integer", "title": "Count", "default": 5, "maximum": 10, "minimum": 1, "description": "Maximum number of recommended voices to return after filtering. Must be between 1 and 10. Fewer than `count` voices may be returned when there are not enough matching candidates." }, "example": 5, "required": false, "description": "Maximum number of recommended voices to return after filtering. Must be between 1 and 10. Fewer than `count` voices may be returned when there are not enough matching candidates." } ], "description": "This API recommends the best Typecast voices based on text descriptions.\n\nInstead of manually searching for a specific `voice_id`, you can find the perfect voice by simply inputting keywords or sentences describing the desired style, mood, language, or use case. The response is sorted by recommendation score, so you can pass the top candidate directly to text-to-speech endpoints such as `POST /v1/text-to-speech`.\n\nThe response includes only `voice_id`, `voice_name`, and `score`. Use `GET /v2/voices` or `GET /v2/voices/{voice_id}` when you need detailed metadata such as supported models, emotions, gender, age, or use cases for the recommended voices.", "operationId": "recommend_voices_v1_voices_recommendations_get", "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request GET \\\n --url 'https://api.typecast.ai/v1/voices/recommendations?query=warm%20female%20voice%20for%20product%20tutorial&count=5' \\\n --header 'X-API-KEY: '\n" }, { "lang": "Python", "label": "Python (requests)", "source": "import requests\n\nresponse = requests.get(\n \"https://api.typecast.ai/v1/voices/recommendations\",\n headers={\"X-API-KEY\": \"\"},\n params={\n \"query\": \"warm female voice for product tutorial\",\n \"count\": 5,\n },\n timeout=30,\n)\nresponse.raise_for_status()\n\nrecommendations = response.json()\nif recommendations:\n print(recommendations[0][\"voice_id\"])\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "Maximum number of concurrent requests allowed" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "Maximum number of custom voices (created via instant cloning) allowed on the current plan." }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "Usage limit information" }, "Prompt": { "title": "Prompt (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "Emotion preset to apply.\r\n\r\nSupported emotions for ssfm-v21: normal, happy, sad, angry\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "example": 1, "description": "Controls the strength of emotional expression (0.0 to 2.0).\r\n\r\n- 0.0: Completely neutral\r\n- 1.0: Standard expression (default)\r\n- 2.0: Maximum intensity\r\n" } }, "description": "Emotion and style settings for the generated speech." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "Age group classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **child**: Child voice (under 12 years old)\n- **teenager**: Teenage voice (13-19 years old)\n- **young_adult**: Young adult voice (20-35 years old)\n- **middle_age**: Middle-aged voice (36-60 years old)\n- **elder**: Elder voice (over 60 years old)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "Total monthly credits provided by the plan" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "Number of credits used" } }, "description": "Credit usage information" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group classification (child/teenager/young_adult/middle_age/elder)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender classification (male/female)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "List of supported TTS models with their available emotions (e.g., [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix (e.g., `tc_60e5426de8b95f1d3000d7b5`); cloned custom voices created via `POST /v1/voices/clone` use the `uc_` prefix and are also returned by `/v2/voices` for the owner." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "List of use case categories this voice is suitable for" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "Human-readable name of the voice" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type — `original` for Typecast-provided stock voices, `custom` for user-cloned voices." } }, "description": "V2 Voice response model with model-grouped emotions and enhanced metadata" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender (`male` or `female`)." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "Supported TTS models and their available emotions." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix and custom voices use the `uc_` prefix." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "Recommended use-case categories." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "Localized voice names, such as `{\"eng\": \"Daejin\", \"kor\": \"대진\"}`." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type (`original` or `custom`)." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "Preview audio URL when available." } }, "description": "Voice metadata with localized names and a preview audio URL." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "Subscription plan for the API.\n\nAvailable values:\n- **free**: Free tier with limited credits\n- **lite**: Lite plan with moderate credits\n- **plus**: Plus plan with higher credits\n- **custom**: Custom enterprise plan" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "TTS model version to use for speech synthesis. Different models offer varying capabilities and quality levels.\n\nAvailable models:\n- **ssfm-v30**: Latest model with improved prosody and additional emotion presets (recommended)\n- **ssfm-v21**: Stable production model with proven reliability and consistent quality\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS model version (e.g., ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "List of supported emotions for this model" } }, "description": "Model information including version and supported emotions" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "English voice name." }, "kor": { "type": "string", "title": "Kor", "description": "Korean voice name." } }, "description": "Localized voice names keyed by ISO 639-3 language code." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "Voice type classification.\n\n- `original` — Typecast-provided stock voices available to every account.\n- `custom` — Voices the user created by uploading or cloning their own sample." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "Gender classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **male**: Male voice\n- **female**: Female voice\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "Available emotion presets for speech synthesis. Each emotion affects the tone, pace, and expressiveness of the generated speech.\n\n**ssfm-v21 Supported Emotions (4 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n\n**ssfm-v30 Supported Emotions (7 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n- whisper: Soft, quiet speech\n- toneup: Higher tonal emphasis\n- tonedown: Lower tonal emphasis\n\nCheck available emotions for each voice through the /v2/voices API response.\n" }, "SmartPrompt": { "type": "object", "title": "SmartPrompt (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "Text that comes AFTER the `text` field in TTSRequest. Provides forward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model anticipate emotional transitions\r\n- Leave empty if no following context is available\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "Discriminator field to identify the prompt type. Must be set to \"smart\" for context-aware emotion inference.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "Text that comes BEFORE the `text` field in TTSRequest. Provides backward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model understand emotional build-up and context\r\n- Leave empty if no preceding context is available\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "PresetPrompt (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "Discriminator field to identify the prompt type. Must be set to \"preset\" for preset-based emotion control.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "Emotion preset to apply to the generated speech.\r\n\r\nSupported emotions: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "Controls the strength of emotional expression in the generated speech.\r\n\r\n- 0.0: Completely neutral, no emotional coloring\r\n- 0.5: Subtle emotional hints\r\n- 1.0: Standard emotional expression (default)\r\n- 1.5: Strong emotional emphasis\r\n- 2.0: Maximum intensity, highly expressive\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "Voice use case categories for content type filtering. Each voice is tagged with one or more use cases indicating its suitability for specific content types.\n\n**Available Use Cases:**\n- **Announcer**: Public announcements and presentations\n- **Anime**: Animation and character voices\n- **Audiobook**: Long-form narration and storytelling\n- **Conversational**: Chatbots and conversational AI\n- **Documentary**: Documentary narration and commentary\n- **E-learning**: Educational content and tutorials\n- **Rapper**: Rap and music performance\n- **Game**: Video game characters and narration\n- **Tiktok/Reels**: Short-form social media content\n- **News**: News broadcasting\n- **Podcast**: Broadcasting and podcast production\n- **Voicemail**: IVR systems and voice assistants\n- **Ads**: Advertising and promotional content\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "Error message describing the issue" }, "message": { "type": "string", "description": "Human-readable message for structured errors" }, "error_code": { "type": "string", "description": "Machine-readable error code for structured errors" } }, "description": "API errors use `detail` for legacy and validation errors, or `error_code` and `message` for structured errors." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "Failure reason when `status` is `failed`." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "source": { "type": "string", "title": "Source", "description": "Creation method (`instant` or `professional`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "Creation time in UTC." } }, "description": "A custom voice owned by the authenticated account." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "Recommendation relevance score. Higher values indicate a stronger match for the query." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "Typecast voice identifier with the `tc_` prefix. Use this value as `voice_id` in text-to-speech requests." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "Human-readable voice name." } }, "description": "Recommended voice candidate returned by `GET /v1/voices/recommendations`, sorted by relevance." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "Current creation or training status of a custom voice." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Human-readable voice name (1-30 characters)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "Engine model the voice was cloned for (`ssfm-v21` or `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "Custom voice identifier with the `uc_` prefix. Use this value as `voice_id` in `POST /v1/text-to-speech` and other endpoints that accept `voice_id`." } }, "description": "Response of `POST /v1/voices/clone` — custom voice metadata returned by instant cloning." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "Segment discriminator. Always `pause`." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "Pause duration in seconds. Each pause may be up to 10 seconds; all pauses combined may be up to 60 seconds.", "exclusiveMinimum": 0 } }, "description": "A silent interval inserted without consuming credits.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single word-level alignment segment between the original transcript and the generated audio." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "Current subscription plan" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "Usage limit information" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "Credit usage information" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "Subscription information response model" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation and whitespace)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single character-level alignment segment between the original transcript and the generated audio." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." } }, "description": "Result returned after creating an instant or professional custom voice." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "Base64-encoded audio bytes. Decode and write to a file using the `audio_format` extension." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "Word-level timestamps (with attached punctuation). `null` when the request uses `granularity=char`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "Character-level timestamps (including punctuation and whitespace). `null` when the request uses `granularity=word`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "Audio encoding format of the bytes in `audio` — either `wav` or `mp3`, mirroring the request's `output.audio_format`." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "Length of the generated audio in seconds." } }, "description": "Response payload for POST /v1/text-to-speech/with-timestamps — base64-encoded audio plus per-word and per-character timestamps aligned with the generated speech." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "Audio sample. WAV or MP3, max 25 MB, 5-150 seconds.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "Voice name (1-30 characters)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "Engine model the voice is cloned for (`ssfm-v21` or `ssfm-v30`)." } }, "description": "Multipart request body for instant cloning." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "One WAV or MP3 recording, 25 MiB or less and 5 to 150 seconds long.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "Product Narrator", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." } }, "description": "Multipart request body for instant voice cloning." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "Custom Voice Name", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "Files", "description": "WAV or MP3 recordings used for training. Only one file can be uploaded.\r\n\r\n**Upload limits**\r\n\r\n* One WAV or MP3 file\r\n* File size: 1 GiB or less\r\n* Duration: 5 minutes to 3 hours\r\n* Sample rate: 16 kHz or higher\r\n\r\nFor best results, we recommend audio that meets the following conditions:\r\n\r\n* Record in a speaking style that closely matches how you want the generated voice to sound.\r\n* Record in a quiet environment without background noise.\r\n* Include only one speaker.\r\n* Record in the language specified in the `language` field.\r\n* Longer input audio results in higher-quality generated voices." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." }, "language": { "type": "string", "title": "Language", "example": "eng", "description": "ISO 639-3 language code, such as `kor` or `eng`." } }, "description": "Multipart request body for professional voice cloning." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "API key for authentication. You can obtain an API key from the Typecast API Console." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Create Professional Clone > Starts asynchronous professional voice training from WAV or MP3 recordings. Send the recordings, display name, supported TTS model, and an ISO 639-3 language code as `multipart/form-data`. **Audio requirements** * One WAV or MP3 file * File size: 1 GiB or less * Duration: 5 minutes to 3 hours * Sample rate: 16 kHz or higher A successful request returns `202 Accepted` with `status: training`. Poll `GET /v1/custom-voices/{voice_id}` until the status becomes `completed` or `failed`. Training may take up to two hours. You’ll receive an email when custom voice training is complete or if it fails. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "Production server" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/custom-voices/professional-clone": { "post": { "tags": [ "Custom Voices" ], "x-mint": { "href": "/api-reference/custom-voices/create-professional-clone" }, "summary": "Create Professional Clone", "security": [ { "ApiKeyAuth": [] } ], "responses": { "202": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomVoiceCreateResponse" }, "example": { "name": "Custom Voice Name", "model": "ssfm-v30", "status": "training", "voice_id": "uc_6700000000000000000000bb" } } }, "description": "Professional clone training started successfully" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Authentication failed" }, "403": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Professional voice slot limit exceeded", "error_code": "PROFESSIONAL_VOICE_SLOT_EXCEEDED" } } }, "description": "Professional cloning is unavailable or no professional voice slot remains" }, "413": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "File size exceeds maximum limit", "error_code": "AUDIO_FILE_TOO_LARGE" } } }, "description": "The combined upload exceeds the configured size limit" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Requested language is not supported for the selected model", "error_code": "LANGUAGE_NOT_SUPPORTED" } } }, "description": "The language, form, audio format, or selected model is not supported" }, "503": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Server is temporarily busy. Please retry shortly.", "error_code": "SERVER_BUSY" } } }, "description": "The professional cloning service is temporarily at capacity" } }, "deprecated": false, "description": "Starts asynchronous professional voice training from WAV or MP3 recordings. Send the recordings, display name, supported TTS model, and an ISO 639-3 language code as `multipart/form-data`.\r\n\r\n**Audio requirements**\r\n\r\n* One WAV or MP3 file\r\n* File size: 1 GiB or less\r\n* Duration: 5 minutes to 3 hours\r\n* Sample rate: 16 kHz or higher\r\n\r\nA successful request returns `202 Accepted` with `status: training`. Poll `GET /v1/custom-voices/{voice_id}` until the status becomes `completed` or `failed`. \r\n\r\nTraining may take up to two hours. You’ll receive an email when custom voice training is complete or if it fails.", "operationId": "create_professional_clone_v1_custom_voices_professional_clone_post", "requestBody": { "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/Body_create_professional_clone_v1_custom_voices_professional_clone_post" } } }, "required": true, "description": "Multipart form containing training audio and voice settings." }, "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request POST \\\n --url https://api.typecast.ai/v1/custom-voices/professional-clone \\\n --header 'X-API-KEY: ' \\\n --form 'files=@training-audio.wav' \\\n --form 'name=Custom Voice Name' \\\n --form 'model=ssfm-v30' \\\n --form 'language=eng'\n" }, { "lang": "Python", "label": "Python (requests)", "source": "import requests\n\nwith open(\"training-audio.wav\", \"rb\") as audio:\n response = requests.post(\n \"https://api.typecast.ai/v1/custom-voices/professional-clone\",\n headers={\"X-API-KEY\": \"\"},\n files=[(\"files\", audio)],\n data={\"name\": \"Brand Voice\", \"model\": \"ssfm-v30\", \"language\": \"eng\"},\n timeout=120,\n )\nresponse.raise_for_status()\nprint(response.json())\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "Maximum number of concurrent requests allowed" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "Maximum number of custom voices (created via instant cloning) allowed on the current plan." }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "Usage limit information" }, "Prompt": { "title": "Prompt (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "Emotion preset to apply.\r\n\r\nSupported emotions for ssfm-v21: normal, happy, sad, angry\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "example": 1, "description": "Controls the strength of emotional expression (0.0 to 2.0).\r\n\r\n- 0.0: Completely neutral\r\n- 1.0: Standard expression (default)\r\n- 2.0: Maximum intensity\r\n" } }, "description": "Emotion and style settings for the generated speech." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "Age group classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **child**: Child voice (under 12 years old)\n- **teenager**: Teenage voice (13-19 years old)\n- **young_adult**: Young adult voice (20-35 years old)\n- **middle_age**: Middle-aged voice (36-60 years old)\n- **elder**: Elder voice (over 60 years old)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "Total monthly credits provided by the plan" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "Number of credits used" } }, "description": "Credit usage information" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group classification (child/teenager/young_adult/middle_age/elder)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender classification (male/female)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "List of supported TTS models with their available emotions (e.g., [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix (e.g., `tc_60e5426de8b95f1d3000d7b5`); cloned custom voices created via `POST /v1/voices/clone` use the `uc_` prefix and are also returned by `/v2/voices` for the owner." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "List of use case categories this voice is suitable for" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "Human-readable name of the voice" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type — `original` for Typecast-provided stock voices, `custom` for user-cloned voices." } }, "description": "V2 Voice response model with model-grouped emotions and enhanced metadata" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender (`male` or `female`)." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "Supported TTS models and their available emotions." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix and custom voices use the `uc_` prefix." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "Recommended use-case categories." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "Localized voice names, such as `{\"eng\": \"Daejin\", \"kor\": \"대진\"}`." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type (`original` or `custom`)." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "Preview audio URL when available." } }, "description": "Voice metadata with localized names and a preview audio URL." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "Subscription plan for the API.\n\nAvailable values:\n- **free**: Free tier with limited credits\n- **lite**: Lite plan with moderate credits\n- **plus**: Plus plan with higher credits\n- **custom**: Custom enterprise plan" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "TTS model version to use for speech synthesis. Different models offer varying capabilities and quality levels.\n\nAvailable models:\n- **ssfm-v30**: Latest model with improved prosody and additional emotion presets (recommended)\n- **ssfm-v21**: Stable production model with proven reliability and consistent quality\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS model version (e.g., ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "List of supported emotions for this model" } }, "description": "Model information including version and supported emotions" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "English voice name." }, "kor": { "type": "string", "title": "Kor", "description": "Korean voice name." } }, "description": "Localized voice names keyed by ISO 639-3 language code." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "Voice type classification.\n\n- `original` — Typecast-provided stock voices available to every account.\n- `custom` — Voices the user created by uploading or cloning their own sample." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "Gender classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **male**: Male voice\n- **female**: Female voice\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "Available emotion presets for speech synthesis. Each emotion affects the tone, pace, and expressiveness of the generated speech.\n\n**ssfm-v21 Supported Emotions (4 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n\n**ssfm-v30 Supported Emotions (7 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n- whisper: Soft, quiet speech\n- toneup: Higher tonal emphasis\n- tonedown: Lower tonal emphasis\n\nCheck available emotions for each voice through the /v2/voices API response.\n" }, "SmartPrompt": { "type": "object", "title": "SmartPrompt (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "Text that comes AFTER the `text` field in TTSRequest. Provides forward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model anticipate emotional transitions\r\n- Leave empty if no following context is available\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "Discriminator field to identify the prompt type. Must be set to \"smart\" for context-aware emotion inference.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "Text that comes BEFORE the `text` field in TTSRequest. Provides backward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model understand emotional build-up and context\r\n- Leave empty if no preceding context is available\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "PresetPrompt (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "Discriminator field to identify the prompt type. Must be set to \"preset\" for preset-based emotion control.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "Emotion preset to apply to the generated speech.\r\n\r\nSupported emotions: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "Controls the strength of emotional expression in the generated speech.\r\n\r\n- 0.0: Completely neutral, no emotional coloring\r\n- 0.5: Subtle emotional hints\r\n- 1.0: Standard emotional expression (default)\r\n- 1.5: Strong emotional emphasis\r\n- 2.0: Maximum intensity, highly expressive\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "Voice use case categories for content type filtering. Each voice is tagged with one or more use cases indicating its suitability for specific content types.\n\n**Available Use Cases:**\n- **Announcer**: Public announcements and presentations\n- **Anime**: Animation and character voices\n- **Audiobook**: Long-form narration and storytelling\n- **Conversational**: Chatbots and conversational AI\n- **Documentary**: Documentary narration and commentary\n- **E-learning**: Educational content and tutorials\n- **Rapper**: Rap and music performance\n- **Game**: Video game characters and narration\n- **Tiktok/Reels**: Short-form social media content\n- **News**: News broadcasting\n- **Podcast**: Broadcasting and podcast production\n- **Voicemail**: IVR systems and voice assistants\n- **Ads**: Advertising and promotional content\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "Error message describing the issue" }, "message": { "type": "string", "description": "Human-readable message for structured errors" }, "error_code": { "type": "string", "description": "Machine-readable error code for structured errors" } }, "description": "API errors use `detail` for legacy and validation errors, or `error_code` and `message` for structured errors." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "Failure reason when `status` is `failed`." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "source": { "type": "string", "title": "Source", "description": "Creation method (`instant` or `professional`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "Creation time in UTC." } }, "description": "A custom voice owned by the authenticated account." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "Recommendation relevance score. Higher values indicate a stronger match for the query." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "Typecast voice identifier with the `tc_` prefix. Use this value as `voice_id` in text-to-speech requests." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "Human-readable voice name." } }, "description": "Recommended voice candidate returned by `GET /v1/voices/recommendations`, sorted by relevance." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "Current creation or training status of a custom voice." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Human-readable voice name (1-30 characters)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "Engine model the voice was cloned for (`ssfm-v21` or `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "Custom voice identifier with the `uc_` prefix. Use this value as `voice_id` in `POST /v1/text-to-speech` and other endpoints that accept `voice_id`." } }, "description": "Response of `POST /v1/voices/clone` — custom voice metadata returned by instant cloning." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "Segment discriminator. Always `pause`." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "Pause duration in seconds. Each pause may be up to 10 seconds; all pauses combined may be up to 60 seconds.", "exclusiveMinimum": 0 } }, "description": "A silent interval inserted without consuming credits.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single word-level alignment segment between the original transcript and the generated audio." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "Current subscription plan" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "Usage limit information" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "Credit usage information" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "Subscription information response model" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation and whitespace)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single character-level alignment segment between the original transcript and the generated audio." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." } }, "description": "Result returned after creating an instant or professional custom voice." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "Base64-encoded audio bytes. Decode and write to a file using the `audio_format` extension." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "Word-level timestamps (with attached punctuation). `null` when the request uses `granularity=char`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "Character-level timestamps (including punctuation and whitespace). `null` when the request uses `granularity=word`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "Audio encoding format of the bytes in `audio` — either `wav` or `mp3`, mirroring the request's `output.audio_format`." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "Length of the generated audio in seconds." } }, "description": "Response payload for POST /v1/text-to-speech/with-timestamps — base64-encoded audio plus per-word and per-character timestamps aligned with the generated speech." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "Audio sample. WAV or MP3, max 25 MB, 5-150 seconds.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "Voice name (1-30 characters)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "Engine model the voice is cloned for (`ssfm-v21` or `ssfm-v30`)." } }, "description": "Multipart request body for instant cloning." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "One WAV or MP3 recording, 25 MiB or less and 5 to 150 seconds long.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "Product Narrator", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." } }, "description": "Multipart request body for instant voice cloning." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "Custom Voice Name", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "Files", "description": "WAV or MP3 recordings used for training. Only one file can be uploaded.\r\n\r\n**Upload limits**\r\n\r\n* One WAV or MP3 file\r\n* File size: 1 GiB or less\r\n* Duration: 5 minutes to 3 hours\r\n* Sample rate: 16 kHz or higher\r\n\r\nFor best results, we recommend audio that meets the following conditions:\r\n\r\n* Record in a speaking style that closely matches how you want the generated voice to sound.\r\n* Record in a quiet environment without background noise.\r\n* Include only one speaker.\r\n* Record in the language specified in the `language` field.\r\n* Longer input audio results in higher-quality generated voices." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." }, "language": { "type": "string", "title": "Language", "example": "eng", "description": "ISO 639-3 language code, such as `kor` or `eng`." } }, "description": "Multipart request body for professional voice cloning." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "API key for authentication. You can obtain an API key from the Typecast API Console." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Create Instant Clone > Creates a synthesis-ready custom voice from one WAV or MP3 recording. Send the audio, a display name, and a supported TTS model as `multipart/form-data`. **Audio requirements** - One WAV or MP3 file - File size: 25 MiB or less - Duration: 5 to 150 seconds The request uses one custom voice slot. A successful response returns the completed voice immediately. Supported models may vary by account and release availability. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "Production server" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/custom-voices/instant-clone": { "post": { "tags": [ "Custom Voices" ], "x-mint": { "href": "/api-reference/custom-voices/create-instant-clone" }, "summary": "Create Instant Clone", "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomVoiceCreateResponse" }, "example": { "name": "Product Narrator", "model": "ssfm-v30", "status": "completed", "voice_id": "uc_6700000000000000000000aa" } } }, "description": "Instant clone created successfully" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Authentication failed" }, "403": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Custom voice slot limit exceeded", "error_code": "CUSTOM_VOICE_SLOT_EXCEEDED" } } }, "description": "Custom voice creation is unavailable or no custom voice slot remains" }, "413": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "File size exceeds maximum limit", "error_code": "AUDIO_FILE_TOO_LARGE" } } }, "description": "The uploaded audio exceeds the configured size limit" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Requested model is not supported for this voice", "error_code": "VOICE_MODEL_NOT_SUPPORTED" } } }, "description": "The form, audio format, or selected model is not supported" }, "503": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Server is temporarily busy. Please retry shortly.", "error_code": "SERVER_BUSY" } } }, "description": "The cloning service is temporarily at capacity" } }, "description": "Creates a synthesis-ready custom voice from one WAV or MP3 recording. Send the audio, a display name, and a supported TTS model as `multipart/form-data`.\n\n**Audio requirements**\n- One WAV or MP3 file\n- File size: 25 MiB or less\n- Duration: 5 to 150 seconds\n\nThe request uses one custom voice slot. A successful response returns the completed voice immediately. Supported models may vary by account and release availability.", "operationId": "create_instant_clone_v1_custom_voices_instant_clone_post", "requestBody": { "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/Body_create_instant_clone_v1_custom_voices_instant_clone_post" } } }, "required": true, "description": "Multipart form containing the source recording and voice settings." }, "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request POST \\\n --url https://api.typecast.ai/v1/custom-voices/instant-clone \\\n --header 'X-API-KEY: ' \\\n --form 'file=@voice-sample.wav' \\\n --form 'name=Product Narrator' \\\n --form 'model=ssfm-v30'\n" }, { "lang": "Python", "label": "Python (requests)", "source": "import requests\n\nwith open(\"voice-sample.wav\", \"rb\") as audio:\n response = requests.post(\n \"https://api.typecast.ai/v1/custom-voices/instant-clone\",\n headers={\"X-API-KEY\": \"\"},\n files={\"file\": audio},\n data={\"name\": \"Product Narrator\", \"model\": \"ssfm-v30\"},\n timeout=120,\n )\nresponse.raise_for_status()\nprint(response.json())\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "Maximum number of concurrent requests allowed" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "Maximum number of custom voices (created via instant cloning) allowed on the current plan." }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "Usage limit information" }, "Prompt": { "title": "Prompt (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "Emotion preset to apply.\r\n\r\nSupported emotions for ssfm-v21: normal, happy, sad, angry\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "example": 1, "description": "Controls the strength of emotional expression (0.0 to 2.0).\r\n\r\n- 0.0: Completely neutral\r\n- 1.0: Standard expression (default)\r\n- 2.0: Maximum intensity\r\n" } }, "description": "Emotion and style settings for the generated speech." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "Age group classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **child**: Child voice (under 12 years old)\n- **teenager**: Teenage voice (13-19 years old)\n- **young_adult**: Young adult voice (20-35 years old)\n- **middle_age**: Middle-aged voice (36-60 years old)\n- **elder**: Elder voice (over 60 years old)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "Total monthly credits provided by the plan" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "Number of credits used" } }, "description": "Credit usage information" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group classification (child/teenager/young_adult/middle_age/elder)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender classification (male/female)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "List of supported TTS models with their available emotions (e.g., [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix (e.g., `tc_60e5426de8b95f1d3000d7b5`); cloned custom voices created via `POST /v1/voices/clone` use the `uc_` prefix and are also returned by `/v2/voices` for the owner." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "List of use case categories this voice is suitable for" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "Human-readable name of the voice" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type — `original` for Typecast-provided stock voices, `custom` for user-cloned voices." } }, "description": "V2 Voice response model with model-grouped emotions and enhanced metadata" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender (`male` or `female`)." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "Supported TTS models and their available emotions." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix and custom voices use the `uc_` prefix." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "Recommended use-case categories." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "Localized voice names, such as `{\"eng\": \"Daejin\", \"kor\": \"대진\"}`." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type (`original` or `custom`)." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "Preview audio URL when available." } }, "description": "Voice metadata with localized names and a preview audio URL." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "Subscription plan for the API.\n\nAvailable values:\n- **free**: Free tier with limited credits\n- **lite**: Lite plan with moderate credits\n- **plus**: Plus plan with higher credits\n- **custom**: Custom enterprise plan" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "TTS model version to use for speech synthesis. Different models offer varying capabilities and quality levels.\n\nAvailable models:\n- **ssfm-v30**: Latest model with improved prosody and additional emotion presets (recommended)\n- **ssfm-v21**: Stable production model with proven reliability and consistent quality\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS model version (e.g., ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "List of supported emotions for this model" } }, "description": "Model information including version and supported emotions" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "English voice name." }, "kor": { "type": "string", "title": "Kor", "description": "Korean voice name." } }, "description": "Localized voice names keyed by ISO 639-3 language code." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "Voice type classification.\n\n- `original` — Typecast-provided stock voices available to every account.\n- `custom` — Voices the user created by uploading or cloning their own sample." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "Gender classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **male**: Male voice\n- **female**: Female voice\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "Available emotion presets for speech synthesis. Each emotion affects the tone, pace, and expressiveness of the generated speech.\n\n**ssfm-v21 Supported Emotions (4 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n\n**ssfm-v30 Supported Emotions (7 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n- whisper: Soft, quiet speech\n- toneup: Higher tonal emphasis\n- tonedown: Lower tonal emphasis\n\nCheck available emotions for each voice through the /v2/voices API response.\n" }, "SmartPrompt": { "type": "object", "title": "SmartPrompt (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "Text that comes AFTER the `text` field in TTSRequest. Provides forward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model anticipate emotional transitions\r\n- Leave empty if no following context is available\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "Discriminator field to identify the prompt type. Must be set to \"smart\" for context-aware emotion inference.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "Text that comes BEFORE the `text` field in TTSRequest. Provides backward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model understand emotional build-up and context\r\n- Leave empty if no preceding context is available\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "PresetPrompt (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "Discriminator field to identify the prompt type. Must be set to \"preset\" for preset-based emotion control.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "Emotion preset to apply to the generated speech.\r\n\r\nSupported emotions: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "Controls the strength of emotional expression in the generated speech.\r\n\r\n- 0.0: Completely neutral, no emotional coloring\r\n- 0.5: Subtle emotional hints\r\n- 1.0: Standard emotional expression (default)\r\n- 1.5: Strong emotional emphasis\r\n- 2.0: Maximum intensity, highly expressive\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "Voice use case categories for content type filtering. Each voice is tagged with one or more use cases indicating its suitability for specific content types.\n\n**Available Use Cases:**\n- **Announcer**: Public announcements and presentations\n- **Anime**: Animation and character voices\n- **Audiobook**: Long-form narration and storytelling\n- **Conversational**: Chatbots and conversational AI\n- **Documentary**: Documentary narration and commentary\n- **E-learning**: Educational content and tutorials\n- **Rapper**: Rap and music performance\n- **Game**: Video game characters and narration\n- **Tiktok/Reels**: Short-form social media content\n- **News**: News broadcasting\n- **Podcast**: Broadcasting and podcast production\n- **Voicemail**: IVR systems and voice assistants\n- **Ads**: Advertising and promotional content\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "Error message describing the issue" }, "message": { "type": "string", "description": "Human-readable message for structured errors" }, "error_code": { "type": "string", "description": "Machine-readable error code for structured errors" } }, "description": "API errors use `detail` for legacy and validation errors, or `error_code` and `message` for structured errors." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "Failure reason when `status` is `failed`." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "source": { "type": "string", "title": "Source", "description": "Creation method (`instant` or `professional`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "Creation time in UTC." } }, "description": "A custom voice owned by the authenticated account." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "Recommendation relevance score. Higher values indicate a stronger match for the query." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "Typecast voice identifier with the `tc_` prefix. Use this value as `voice_id` in text-to-speech requests." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "Human-readable voice name." } }, "description": "Recommended voice candidate returned by `GET /v1/voices/recommendations`, sorted by relevance." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "Current creation or training status of a custom voice." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Human-readable voice name (1-30 characters)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "Engine model the voice was cloned for (`ssfm-v21` or `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "Custom voice identifier with the `uc_` prefix. Use this value as `voice_id` in `POST /v1/text-to-speech` and other endpoints that accept `voice_id`." } }, "description": "Response of `POST /v1/voices/clone` — custom voice metadata returned by instant cloning." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "Segment discriminator. Always `pause`." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "Pause duration in seconds. Each pause may be up to 10 seconds; all pauses combined may be up to 60 seconds.", "exclusiveMinimum": 0 } }, "description": "A silent interval inserted without consuming credits.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single word-level alignment segment between the original transcript and the generated audio." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "Current subscription plan" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "Usage limit information" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "Credit usage information" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "Subscription information response model" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation and whitespace)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single character-level alignment segment between the original transcript and the generated audio." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." } }, "description": "Result returned after creating an instant or professional custom voice." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "Base64-encoded audio bytes. Decode and write to a file using the `audio_format` extension." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "Word-level timestamps (with attached punctuation). `null` when the request uses `granularity=char`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "Character-level timestamps (including punctuation and whitespace). `null` when the request uses `granularity=word`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "Audio encoding format of the bytes in `audio` — either `wav` or `mp3`, mirroring the request's `output.audio_format`." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "Length of the generated audio in seconds." } }, "description": "Response payload for POST /v1/text-to-speech/with-timestamps — base64-encoded audio plus per-word and per-character timestamps aligned with the generated speech." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "Audio sample. WAV or MP3, max 25 MB, 5-150 seconds.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "Voice name (1-30 characters)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "Engine model the voice is cloned for (`ssfm-v21` or `ssfm-v30`)." } }, "description": "Multipart request body for instant cloning." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "One WAV or MP3 recording, 25 MiB or less and 5 to 150 seconds long.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "Product Narrator", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." } }, "description": "Multipart request body for instant voice cloning." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "Custom Voice Name", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "Files", "description": "WAV or MP3 recordings used for training. Only one file can be uploaded.\r\n\r\n**Upload limits**\r\n\r\n* One WAV or MP3 file\r\n* File size: 1 GiB or less\r\n* Duration: 5 minutes to 3 hours\r\n* Sample rate: 16 kHz or higher\r\n\r\nFor best results, we recommend audio that meets the following conditions:\r\n\r\n* Record in a speaking style that closely matches how you want the generated voice to sound.\r\n* Record in a quiet environment without background noise.\r\n* Include only one speaker.\r\n* Record in the language specified in the `language` field.\r\n* Longer input audio results in higher-quality generated voices." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." }, "language": { "type": "string", "title": "Language", "example": "eng", "description": "ISO 639-3 language code, such as `kor` or `eng`." } }, "description": "Multipart request body for professional voice cloning." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "API key for authentication. You can obtain an API key from the Typecast API Console." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # List Custom Voices > Lists all non-deleted custom voices owned by the authenticated account, including instant and professional clones. Results can include `pending`, `training`, `completed`, and `failed` voices. Use `status` to decide whether a voice is ready for synthesis. When `status` is `failed`, `error` contains a safe failure reason; otherwise it is `null`. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "Production server" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/custom-voices": { "get": { "tags": [ "Custom Voices" ], "x-mint": { "href": "/api-reference/custom-voices/list-custom-voices" }, "summary": "List Custom Voices", "responses": { "200": { "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CustomVoiceItem" }, "title": "Response List Custom Voices V1 Custom Voices Get" }, "example": [ { "name": "Product Narrator", "error": null, "model": "ssfm-v30", "source": "instant", "status": "completed", "voice_id": "uc_6700000000000000000000aa", "created_at": "2026-08-26T04:15:00Z" }, { "name": "Brand Voice", "error": null, "model": "ssfm-v30", "source": "professional", "status": "training", "voice_id": "uc_6700000000000000000000bb", "created_at": "2026-08-26T04:20:00Z" } ] } }, "description": "Custom voices retrieved successfully" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Authentication failed" } }, "description": "Lists all non-deleted custom voices owned by the authenticated account, including instant and professional clones. Results can include `pending`, `training`, `completed`, and `failed` voices.\n\nUse `status` to decide whether a voice is ready for synthesis. When `status` is `failed`, `error` contains a safe failure reason; otherwise it is `null`.", "operationId": "list_custom_voices_v1_custom_voices_get", "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request GET \\\n --url https://api.typecast.ai/v1/custom-voices \\\n --header 'X-API-KEY: '\n" }, { "lang": "Python", "label": "Python (requests)", "source": "import requests\n\nresponse = requests.get(\n \"https://api.typecast.ai/v1/custom-voices\",\n headers={\"X-API-KEY\": \"\"},\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "Maximum number of concurrent requests allowed" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "Maximum number of custom voices (created via instant cloning) allowed on the current plan." }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "Usage limit information" }, "Prompt": { "title": "Prompt (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "Emotion preset to apply.\r\n\r\nSupported emotions for ssfm-v21: normal, happy, sad, angry\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "example": 1, "description": "Controls the strength of emotional expression (0.0 to 2.0).\r\n\r\n- 0.0: Completely neutral\r\n- 1.0: Standard expression (default)\r\n- 2.0: Maximum intensity\r\n" } }, "description": "Emotion and style settings for the generated speech." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "Age group classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **child**: Child voice (under 12 years old)\n- **teenager**: Teenage voice (13-19 years old)\n- **young_adult**: Young adult voice (20-35 years old)\n- **middle_age**: Middle-aged voice (36-60 years old)\n- **elder**: Elder voice (over 60 years old)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "Total monthly credits provided by the plan" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "Number of credits used" } }, "description": "Credit usage information" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group classification (child/teenager/young_adult/middle_age/elder)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender classification (male/female)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "List of supported TTS models with their available emotions (e.g., [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix (e.g., `tc_60e5426de8b95f1d3000d7b5`); cloned custom voices created via `POST /v1/voices/clone` use the `uc_` prefix and are also returned by `/v2/voices` for the owner." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "List of use case categories this voice is suitable for" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "Human-readable name of the voice" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type — `original` for Typecast-provided stock voices, `custom` for user-cloned voices." } }, "description": "V2 Voice response model with model-grouped emotions and enhanced metadata" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender (`male` or `female`)." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "Supported TTS models and their available emotions." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix and custom voices use the `uc_` prefix." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "Recommended use-case categories." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "Localized voice names, such as `{\"eng\": \"Daejin\", \"kor\": \"대진\"}`." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type (`original` or `custom`)." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "Preview audio URL when available." } }, "description": "Voice metadata with localized names and a preview audio URL." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "Subscription plan for the API.\n\nAvailable values:\n- **free**: Free tier with limited credits\n- **lite**: Lite plan with moderate credits\n- **plus**: Plus plan with higher credits\n- **custom**: Custom enterprise plan" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "TTS model version to use for speech synthesis. Different models offer varying capabilities and quality levels.\n\nAvailable models:\n- **ssfm-v30**: Latest model with improved prosody and additional emotion presets (recommended)\n- **ssfm-v21**: Stable production model with proven reliability and consistent quality\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS model version (e.g., ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "List of supported emotions for this model" } }, "description": "Model information including version and supported emotions" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "English voice name." }, "kor": { "type": "string", "title": "Kor", "description": "Korean voice name." } }, "description": "Localized voice names keyed by ISO 639-3 language code." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "Voice type classification.\n\n- `original` — Typecast-provided stock voices available to every account.\n- `custom` — Voices the user created by uploading or cloning their own sample." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "Gender classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **male**: Male voice\n- **female**: Female voice\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "Available emotion presets for speech synthesis. Each emotion affects the tone, pace, and expressiveness of the generated speech.\n\n**ssfm-v21 Supported Emotions (4 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n\n**ssfm-v30 Supported Emotions (7 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n- whisper: Soft, quiet speech\n- toneup: Higher tonal emphasis\n- tonedown: Lower tonal emphasis\n\nCheck available emotions for each voice through the /v2/voices API response.\n" }, "SmartPrompt": { "type": "object", "title": "SmartPrompt (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "Text that comes AFTER the `text` field in TTSRequest. Provides forward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model anticipate emotional transitions\r\n- Leave empty if no following context is available\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "Discriminator field to identify the prompt type. Must be set to \"smart\" for context-aware emotion inference.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "Text that comes BEFORE the `text` field in TTSRequest. Provides backward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model understand emotional build-up and context\r\n- Leave empty if no preceding context is available\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "PresetPrompt (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "Discriminator field to identify the prompt type. Must be set to \"preset\" for preset-based emotion control.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "Emotion preset to apply to the generated speech.\r\n\r\nSupported emotions: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "Controls the strength of emotional expression in the generated speech.\r\n\r\n- 0.0: Completely neutral, no emotional coloring\r\n- 0.5: Subtle emotional hints\r\n- 1.0: Standard emotional expression (default)\r\n- 1.5: Strong emotional emphasis\r\n- 2.0: Maximum intensity, highly expressive\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "Voice use case categories for content type filtering. Each voice is tagged with one or more use cases indicating its suitability for specific content types.\n\n**Available Use Cases:**\n- **Announcer**: Public announcements and presentations\n- **Anime**: Animation and character voices\n- **Audiobook**: Long-form narration and storytelling\n- **Conversational**: Chatbots and conversational AI\n- **Documentary**: Documentary narration and commentary\n- **E-learning**: Educational content and tutorials\n- **Rapper**: Rap and music performance\n- **Game**: Video game characters and narration\n- **Tiktok/Reels**: Short-form social media content\n- **News**: News broadcasting\n- **Podcast**: Broadcasting and podcast production\n- **Voicemail**: IVR systems and voice assistants\n- **Ads**: Advertising and promotional content\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "Error message describing the issue" }, "message": { "type": "string", "description": "Human-readable message for structured errors" }, "error_code": { "type": "string", "description": "Machine-readable error code for structured errors" } }, "description": "API errors use `detail` for legacy and validation errors, or `error_code` and `message` for structured errors." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "Failure reason when `status` is `failed`." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "source": { "type": "string", "title": "Source", "description": "Creation method (`instant` or `professional`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "Creation time in UTC." } }, "description": "A custom voice owned by the authenticated account." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "Recommendation relevance score. Higher values indicate a stronger match for the query." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "Typecast voice identifier with the `tc_` prefix. Use this value as `voice_id` in text-to-speech requests." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "Human-readable voice name." } }, "description": "Recommended voice candidate returned by `GET /v1/voices/recommendations`, sorted by relevance." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "Current creation or training status of a custom voice." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Human-readable voice name (1-30 characters)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "Engine model the voice was cloned for (`ssfm-v21` or `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "Custom voice identifier with the `uc_` prefix. Use this value as `voice_id` in `POST /v1/text-to-speech` and other endpoints that accept `voice_id`." } }, "description": "Response of `POST /v1/voices/clone` — custom voice metadata returned by instant cloning." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "Segment discriminator. Always `pause`." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "Pause duration in seconds. Each pause may be up to 10 seconds; all pauses combined may be up to 60 seconds.", "exclusiveMinimum": 0 } }, "description": "A silent interval inserted without consuming credits.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single word-level alignment segment between the original transcript and the generated audio." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "Current subscription plan" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "Usage limit information" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "Credit usage information" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "Subscription information response model" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation and whitespace)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single character-level alignment segment between the original transcript and the generated audio." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." } }, "description": "Result returned after creating an instant or professional custom voice." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "Base64-encoded audio bytes. Decode and write to a file using the `audio_format` extension." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "Word-level timestamps (with attached punctuation). `null` when the request uses `granularity=char`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "Character-level timestamps (including punctuation and whitespace). `null` when the request uses `granularity=word`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "Audio encoding format of the bytes in `audio` — either `wav` or `mp3`, mirroring the request's `output.audio_format`." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "Length of the generated audio in seconds." } }, "description": "Response payload for POST /v1/text-to-speech/with-timestamps — base64-encoded audio plus per-word and per-character timestamps aligned with the generated speech." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "Audio sample. WAV or MP3, max 25 MB, 5-150 seconds.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "Voice name (1-30 characters)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "Engine model the voice is cloned for (`ssfm-v21` or `ssfm-v30`)." } }, "description": "Multipart request body for instant cloning." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "One WAV or MP3 recording, 25 MiB or less and 5 to 150 seconds long.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "Product Narrator", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." } }, "description": "Multipart request body for instant voice cloning." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "Custom Voice Name", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "Files", "description": "WAV or MP3 recordings used for training. Only one file can be uploaded.\r\n\r\n**Upload limits**\r\n\r\n* One WAV or MP3 file\r\n* File size: 1 GiB or less\r\n* Duration: 5 minutes to 3 hours\r\n* Sample rate: 16 kHz or higher\r\n\r\nFor best results, we recommend audio that meets the following conditions:\r\n\r\n* Record in a speaking style that closely matches how you want the generated voice to sound.\r\n* Record in a quiet environment without background noise.\r\n* Include only one speaker.\r\n* Record in the language specified in the `language` field.\r\n* Longer input audio results in higher-quality generated voices." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." }, "language": { "type": "string", "title": "Language", "example": "eng", "description": "ISO 639-3 language code, such as `kor` or `eng`." } }, "description": "Multipart request body for professional voice cloning." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "API key for authentication. You can obtain an API key from the Typecast API Console." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Get Custom Voice > Returns one non-deleted custom voice owned by the authenticated account. Poll this endpoint after starting professional cloning and stop when `status` becomes `completed` or `failed`. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "Production server" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/custom-voices/{voice_id}": { "get": { "tags": [ "Custom Voices" ], "x-mint": { "href": "/api-reference/custom-voices/get-custom-voice" }, "summary": "Get Custom Voice", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomVoiceItem" }, "example": { "name": "Brand Voice", "error": null, "model": "ssfm-v30", "source": "professional", "status": "training", "voice_id": "uc_6700000000000000000000bb", "created_at": "2026-08-26T04:20:00Z" } } }, "description": "Custom voice retrieved successfully" }, "400": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Invalid voice ID", "error_code": "INVALID_VOICE_ID" } } }, "description": "The custom voice ID format is invalid" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Authentication failed" }, "404": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Custom voice not found", "error_code": "CUSTOM_VOICE_NOT_FOUND" } } }, "description": "The custom voice does not exist, was deleted, or belongs to another account" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid request format" } } }, "description": "Validation Error - Invalid voice ID" } }, "parameters": [ { "in": "path", "name": "voice_id", "schema": { "type": "string", "title": "Voice Id" }, "required": true, "description": "Unique custom voice identifier." } ], "description": "Returns one non-deleted custom voice owned by the authenticated account. Poll this endpoint after starting professional cloning and stop when `status` becomes `completed` or `failed`.", "operationId": "get_custom_voice_v1_custom_voices__voice_id__get", "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request GET \\\n --url https://api.typecast.ai/v1/custom-voices/uc_6700000000000000000000bb \\\n --header 'X-API-KEY: '\n" }, { "lang": "Python", "label": "Python (requests)", "source": "import requests\n\nresponse = requests.get(\n \"https://api.typecast.ai/v1/custom-voices/uc_6700000000000000000000bb\",\n headers={\"X-API-KEY\": \"\"},\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "Maximum number of concurrent requests allowed" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "Maximum number of custom voices (created via instant cloning) allowed on the current plan." }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "Usage limit information" }, "Prompt": { "title": "Prompt (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "Emotion preset to apply.\r\n\r\nSupported emotions for ssfm-v21: normal, happy, sad, angry\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "example": 1, "description": "Controls the strength of emotional expression (0.0 to 2.0).\r\n\r\n- 0.0: Completely neutral\r\n- 1.0: Standard expression (default)\r\n- 2.0: Maximum intensity\r\n" } }, "description": "Emotion and style settings for the generated speech." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "Age group classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **child**: Child voice (under 12 years old)\n- **teenager**: Teenage voice (13-19 years old)\n- **young_adult**: Young adult voice (20-35 years old)\n- **middle_age**: Middle-aged voice (36-60 years old)\n- **elder**: Elder voice (over 60 years old)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "Total monthly credits provided by the plan" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "Number of credits used" } }, "description": "Credit usage information" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group classification (child/teenager/young_adult/middle_age/elder)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender classification (male/female)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "List of supported TTS models with their available emotions (e.g., [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix (e.g., `tc_60e5426de8b95f1d3000d7b5`); cloned custom voices created via `POST /v1/voices/clone` use the `uc_` prefix and are also returned by `/v2/voices` for the owner." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "List of use case categories this voice is suitable for" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "Human-readable name of the voice" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type — `original` for Typecast-provided stock voices, `custom` for user-cloned voices." } }, "description": "V2 Voice response model with model-grouped emotions and enhanced metadata" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender (`male` or `female`)." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "Supported TTS models and their available emotions." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix and custom voices use the `uc_` prefix." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "Recommended use-case categories." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "Localized voice names, such as `{\"eng\": \"Daejin\", \"kor\": \"대진\"}`." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type (`original` or `custom`)." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "Preview audio URL when available." } }, "description": "Voice metadata with localized names and a preview audio URL." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "Subscription plan for the API.\n\nAvailable values:\n- **free**: Free tier with limited credits\n- **lite**: Lite plan with moderate credits\n- **plus**: Plus plan with higher credits\n- **custom**: Custom enterprise plan" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "TTS model version to use for speech synthesis. Different models offer varying capabilities and quality levels.\n\nAvailable models:\n- **ssfm-v30**: Latest model with improved prosody and additional emotion presets (recommended)\n- **ssfm-v21**: Stable production model with proven reliability and consistent quality\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS model version (e.g., ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "List of supported emotions for this model" } }, "description": "Model information including version and supported emotions" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "English voice name." }, "kor": { "type": "string", "title": "Kor", "description": "Korean voice name." } }, "description": "Localized voice names keyed by ISO 639-3 language code." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "Voice type classification.\n\n- `original` — Typecast-provided stock voices available to every account.\n- `custom` — Voices the user created by uploading or cloning their own sample." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "Gender classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **male**: Male voice\n- **female**: Female voice\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "Available emotion presets for speech synthesis. Each emotion affects the tone, pace, and expressiveness of the generated speech.\n\n**ssfm-v21 Supported Emotions (4 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n\n**ssfm-v30 Supported Emotions (7 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n- whisper: Soft, quiet speech\n- toneup: Higher tonal emphasis\n- tonedown: Lower tonal emphasis\n\nCheck available emotions for each voice through the /v2/voices API response.\n" }, "SmartPrompt": { "type": "object", "title": "SmartPrompt (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "Text that comes AFTER the `text` field in TTSRequest. Provides forward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model anticipate emotional transitions\r\n- Leave empty if no following context is available\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "Discriminator field to identify the prompt type. Must be set to \"smart\" for context-aware emotion inference.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "Text that comes BEFORE the `text` field in TTSRequest. Provides backward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model understand emotional build-up and context\r\n- Leave empty if no preceding context is available\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "PresetPrompt (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "Discriminator field to identify the prompt type. Must be set to \"preset\" for preset-based emotion control.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "Emotion preset to apply to the generated speech.\r\n\r\nSupported emotions: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "Controls the strength of emotional expression in the generated speech.\r\n\r\n- 0.0: Completely neutral, no emotional coloring\r\n- 0.5: Subtle emotional hints\r\n- 1.0: Standard emotional expression (default)\r\n- 1.5: Strong emotional emphasis\r\n- 2.0: Maximum intensity, highly expressive\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "Voice use case categories for content type filtering. Each voice is tagged with one or more use cases indicating its suitability for specific content types.\n\n**Available Use Cases:**\n- **Announcer**: Public announcements and presentations\n- **Anime**: Animation and character voices\n- **Audiobook**: Long-form narration and storytelling\n- **Conversational**: Chatbots and conversational AI\n- **Documentary**: Documentary narration and commentary\n- **E-learning**: Educational content and tutorials\n- **Rapper**: Rap and music performance\n- **Game**: Video game characters and narration\n- **Tiktok/Reels**: Short-form social media content\n- **News**: News broadcasting\n- **Podcast**: Broadcasting and podcast production\n- **Voicemail**: IVR systems and voice assistants\n- **Ads**: Advertising and promotional content\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "Error message describing the issue" }, "message": { "type": "string", "description": "Human-readable message for structured errors" }, "error_code": { "type": "string", "description": "Machine-readable error code for structured errors" } }, "description": "API errors use `detail` for legacy and validation errors, or `error_code` and `message` for structured errors." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "Failure reason when `status` is `failed`." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "source": { "type": "string", "title": "Source", "description": "Creation method (`instant` or `professional`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "Creation time in UTC." } }, "description": "A custom voice owned by the authenticated account." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "Recommendation relevance score. Higher values indicate a stronger match for the query." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "Typecast voice identifier with the `tc_` prefix. Use this value as `voice_id` in text-to-speech requests." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "Human-readable voice name." } }, "description": "Recommended voice candidate returned by `GET /v1/voices/recommendations`, sorted by relevance." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "Current creation or training status of a custom voice." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Human-readable voice name (1-30 characters)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "Engine model the voice was cloned for (`ssfm-v21` or `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "Custom voice identifier with the `uc_` prefix. Use this value as `voice_id` in `POST /v1/text-to-speech` and other endpoints that accept `voice_id`." } }, "description": "Response of `POST /v1/voices/clone` — custom voice metadata returned by instant cloning." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "Segment discriminator. Always `pause`." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "Pause duration in seconds. Each pause may be up to 10 seconds; all pauses combined may be up to 60 seconds.", "exclusiveMinimum": 0 } }, "description": "A silent interval inserted without consuming credits.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single word-level alignment segment between the original transcript and the generated audio." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "Current subscription plan" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "Usage limit information" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "Credit usage information" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "Subscription information response model" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation and whitespace)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single character-level alignment segment between the original transcript and the generated audio." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." } }, "description": "Result returned after creating an instant or professional custom voice." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "Base64-encoded audio bytes. Decode and write to a file using the `audio_format` extension." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "Word-level timestamps (with attached punctuation). `null` when the request uses `granularity=char`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "Character-level timestamps (including punctuation and whitespace). `null` when the request uses `granularity=word`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "Audio encoding format of the bytes in `audio` — either `wav` or `mp3`, mirroring the request's `output.audio_format`." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "Length of the generated audio in seconds." } }, "description": "Response payload for POST /v1/text-to-speech/with-timestamps — base64-encoded audio plus per-word and per-character timestamps aligned with the generated speech." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "Audio sample. WAV or MP3, max 25 MB, 5-150 seconds.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "Voice name (1-30 characters)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "Engine model the voice is cloned for (`ssfm-v21` or `ssfm-v30`)." } }, "description": "Multipart request body for instant cloning." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "One WAV or MP3 recording, 25 MiB or less and 5 to 150 seconds long.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "Product Narrator", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." } }, "description": "Multipart request body for instant voice cloning." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "Custom Voice Name", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "Files", "description": "WAV or MP3 recordings used for training. Only one file can be uploaded.\r\n\r\n**Upload limits**\r\n\r\n* One WAV or MP3 file\r\n* File size: 1 GiB or less\r\n* Duration: 5 minutes to 3 hours\r\n* Sample rate: 16 kHz or higher\r\n\r\nFor best results, we recommend audio that meets the following conditions:\r\n\r\n* Record in a speaking style that closely matches how you want the generated voice to sound.\r\n* Record in a quiet environment without background noise.\r\n* Include only one speaker.\r\n* Record in the language specified in the `language` field.\r\n* Longer input audio results in higher-quality generated voices." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." }, "language": { "type": "string", "title": "Language", "example": "eng", "description": "ISO 639-3 language code, such as `kor` or `eng`." } }, "description": "Multipart request body for professional voice cloning." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "API key for authentication. You can obtain an API key from the Typecast API Console." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Delete Custom Voice > Soft-deletes one custom voice owned by the authenticated account, freeing its slot. If a professional clone is still training, the training run is canceled after the voice is deleted. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "Production server" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/custom-voices/{voice_id}": { "delete": { "tags": [ "Custom Voices" ], "x-mint": { "href": "/api-reference/custom-voices/delete-custom-voice" }, "summary": "Delete Custom Voice", "responses": { "204": { "description": "Custom voice deleted successfully" }, "400": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Invalid voice ID", "error_code": "INVALID_VOICE_ID" } } }, "description": "The custom voice ID format is invalid" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Authentication failed" }, "404": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Custom voice not found", "error_code": "CUSTOM_VOICE_NOT_FOUND" } } }, "description": "The custom voice does not exist, was deleted, or belongs to another account" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid request format" } } }, "description": "Validation Error - Invalid voice ID" } }, "parameters": [ { "in": "path", "name": "voice_id", "schema": { "type": "string", "title": "Voice Id" }, "required": true, "description": "Unique custom voice identifier." } ], "description": "Soft-deletes one custom voice owned by the authenticated account, freeing its slot. If a professional clone is still training, the training run is canceled after the voice is deleted.", "operationId": "delete_custom_voice_v1_custom_voices__voice_id__delete", "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request DELETE \\\n --url https://api.typecast.ai/v1/custom-voices/uc_6700000000000000000000bb \\\n --header 'X-API-KEY: '\n" }, { "lang": "Python", "label": "Python (requests)", "source": "import requests\n\nresponse = requests.delete(\n \"https://api.typecast.ai/v1/custom-voices/uc_6700000000000000000000bb\",\n headers={\"X-API-KEY\": \"\"},\n timeout=30,\n)\nresponse.raise_for_status()\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "Maximum number of concurrent requests allowed" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "Maximum number of custom voices (created via instant cloning) allowed on the current plan." }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "Usage limit information" }, "Prompt": { "title": "Prompt (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "Emotion preset to apply.\r\n\r\nSupported emotions for ssfm-v21: normal, happy, sad, angry\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "example": 1, "description": "Controls the strength of emotional expression (0.0 to 2.0).\r\n\r\n- 0.0: Completely neutral\r\n- 1.0: Standard expression (default)\r\n- 2.0: Maximum intensity\r\n" } }, "description": "Emotion and style settings for the generated speech." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "Age group classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **child**: Child voice (under 12 years old)\n- **teenager**: Teenage voice (13-19 years old)\n- **young_adult**: Young adult voice (20-35 years old)\n- **middle_age**: Middle-aged voice (36-60 years old)\n- **elder**: Elder voice (over 60 years old)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "Total monthly credits provided by the plan" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "Number of credits used" } }, "description": "Credit usage information" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group classification (child/teenager/young_adult/middle_age/elder)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender classification (male/female)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "List of supported TTS models with their available emotions (e.g., [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix (e.g., `tc_60e5426de8b95f1d3000d7b5`); cloned custom voices created via `POST /v1/voices/clone` use the `uc_` prefix and are also returned by `/v2/voices` for the owner." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "List of use case categories this voice is suitable for" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "Human-readable name of the voice" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type — `original` for Typecast-provided stock voices, `custom` for user-cloned voices." } }, "description": "V2 Voice response model with model-grouped emotions and enhanced metadata" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender (`male` or `female`)." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "Supported TTS models and their available emotions." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix and custom voices use the `uc_` prefix." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "Recommended use-case categories." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "Localized voice names, such as `{\"eng\": \"Daejin\", \"kor\": \"대진\"}`." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type (`original` or `custom`)." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "Preview audio URL when available." } }, "description": "Voice metadata with localized names and a preview audio URL." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "Subscription plan for the API.\n\nAvailable values:\n- **free**: Free tier with limited credits\n- **lite**: Lite plan with moderate credits\n- **plus**: Plus plan with higher credits\n- **custom**: Custom enterprise plan" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "TTS model version to use for speech synthesis. Different models offer varying capabilities and quality levels.\n\nAvailable models:\n- **ssfm-v30**: Latest model with improved prosody and additional emotion presets (recommended)\n- **ssfm-v21**: Stable production model with proven reliability and consistent quality\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS model version (e.g., ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "List of supported emotions for this model" } }, "description": "Model information including version and supported emotions" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "English voice name." }, "kor": { "type": "string", "title": "Kor", "description": "Korean voice name." } }, "description": "Localized voice names keyed by ISO 639-3 language code." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "Voice type classification.\n\n- `original` — Typecast-provided stock voices available to every account.\n- `custom` — Voices the user created by uploading or cloning their own sample." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "Gender classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **male**: Male voice\n- **female**: Female voice\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "Available emotion presets for speech synthesis. Each emotion affects the tone, pace, and expressiveness of the generated speech.\n\n**ssfm-v21 Supported Emotions (4 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n\n**ssfm-v30 Supported Emotions (7 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n- whisper: Soft, quiet speech\n- toneup: Higher tonal emphasis\n- tonedown: Lower tonal emphasis\n\nCheck available emotions for each voice through the /v2/voices API response.\n" }, "SmartPrompt": { "type": "object", "title": "SmartPrompt (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "Text that comes AFTER the `text` field in TTSRequest. Provides forward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model anticipate emotional transitions\r\n- Leave empty if no following context is available\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "Discriminator field to identify the prompt type. Must be set to \"smart\" for context-aware emotion inference.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "Text that comes BEFORE the `text` field in TTSRequest. Provides backward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model understand emotional build-up and context\r\n- Leave empty if no preceding context is available\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "PresetPrompt (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "Discriminator field to identify the prompt type. Must be set to \"preset\" for preset-based emotion control.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "Emotion preset to apply to the generated speech.\r\n\r\nSupported emotions: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "Controls the strength of emotional expression in the generated speech.\r\n\r\n- 0.0: Completely neutral, no emotional coloring\r\n- 0.5: Subtle emotional hints\r\n- 1.0: Standard emotional expression (default)\r\n- 1.5: Strong emotional emphasis\r\n- 2.0: Maximum intensity, highly expressive\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "Voice use case categories for content type filtering. Each voice is tagged with one or more use cases indicating its suitability for specific content types.\n\n**Available Use Cases:**\n- **Announcer**: Public announcements and presentations\n- **Anime**: Animation and character voices\n- **Audiobook**: Long-form narration and storytelling\n- **Conversational**: Chatbots and conversational AI\n- **Documentary**: Documentary narration and commentary\n- **E-learning**: Educational content and tutorials\n- **Rapper**: Rap and music performance\n- **Game**: Video game characters and narration\n- **Tiktok/Reels**: Short-form social media content\n- **News**: News broadcasting\n- **Podcast**: Broadcasting and podcast production\n- **Voicemail**: IVR systems and voice assistants\n- **Ads**: Advertising and promotional content\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "Error message describing the issue" }, "message": { "type": "string", "description": "Human-readable message for structured errors" }, "error_code": { "type": "string", "description": "Machine-readable error code for structured errors" } }, "description": "API errors use `detail` for legacy and validation errors, or `error_code` and `message` for structured errors." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "Failure reason when `status` is `failed`." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "source": { "type": "string", "title": "Source", "description": "Creation method (`instant` or `professional`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "Creation time in UTC." } }, "description": "A custom voice owned by the authenticated account." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "Recommendation relevance score. Higher values indicate a stronger match for the query." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "Typecast voice identifier with the `tc_` prefix. Use this value as `voice_id` in text-to-speech requests." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "Human-readable voice name." } }, "description": "Recommended voice candidate returned by `GET /v1/voices/recommendations`, sorted by relevance." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "Current creation or training status of a custom voice." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Human-readable voice name (1-30 characters)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "Engine model the voice was cloned for (`ssfm-v21` or `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "Custom voice identifier with the `uc_` prefix. Use this value as `voice_id` in `POST /v1/text-to-speech` and other endpoints that accept `voice_id`." } }, "description": "Response of `POST /v1/voices/clone` — custom voice metadata returned by instant cloning." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "Segment discriminator. Always `pause`." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "Pause duration in seconds. Each pause may be up to 10 seconds; all pauses combined may be up to 60 seconds.", "exclusiveMinimum": 0 } }, "description": "A silent interval inserted without consuming credits.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single word-level alignment segment between the original transcript and the generated audio." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "Current subscription plan" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "Usage limit information" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "Credit usage information" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "Subscription information response model" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation and whitespace)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single character-level alignment segment between the original transcript and the generated audio." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." } }, "description": "Result returned after creating an instant or professional custom voice." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "Base64-encoded audio bytes. Decode and write to a file using the `audio_format` extension." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "Word-level timestamps (with attached punctuation). `null` when the request uses `granularity=char`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "Character-level timestamps (including punctuation and whitespace). `null` when the request uses `granularity=word`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "Audio encoding format of the bytes in `audio` — either `wav` or `mp3`, mirroring the request's `output.audio_format`." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "Length of the generated audio in seconds." } }, "description": "Response payload for POST /v1/text-to-speech/with-timestamps — base64-encoded audio plus per-word and per-character timestamps aligned with the generated speech." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "Audio sample. WAV or MP3, max 25 MB, 5-150 seconds.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "Voice name (1-30 characters)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "Engine model the voice is cloned for (`ssfm-v21` or `ssfm-v30`)." } }, "description": "Multipart request body for instant cloning." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "One WAV or MP3 recording, 25 MiB or less and 5 to 150 seconds long.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "Product Narrator", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." } }, "description": "Multipart request body for instant voice cloning." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "Custom Voice Name", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "Files", "description": "WAV or MP3 recordings used for training. Only one file can be uploaded.\r\n\r\n**Upload limits**\r\n\r\n* One WAV or MP3 file\r\n* File size: 1 GiB or less\r\n* Duration: 5 minutes to 3 hours\r\n* Sample rate: 16 kHz or higher\r\n\r\nFor best results, we recommend audio that meets the following conditions:\r\n\r\n* Record in a speaking style that closely matches how you want the generated voice to sound.\r\n* Record in a quiet environment without background noise.\r\n* Include only one speaker.\r\n* Record in the language specified in the `language` field.\r\n* Longer input audio results in higher-quality generated voices." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." }, "language": { "type": "string", "title": "Language", "example": "eng", "description": "ISO 639-3 language code, such as `kor` or `eng`." } }, "description": "Multipart request body for professional voice cloning." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "API key for authentication. You can obtain an API key from the Typecast API Console." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Get Subscription > Retrieve the authenticated user's current subscription information, including plan name, credit usage, concurrency limit, and custom voice slot capacity. Use this endpoint to check remaining credits, verify your current plan, or see how many custom voice slots are available before cloning a new voice with `POST /v1/voices/clone`. The `limits.custom_voice_slot` value is the maximum number of custom voices the current plan can hold at once; free the slot by calling `DELETE /v1/voices/{voice_id}`. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "Production server" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/users/me/subscription": { "get": { "tags": [ "Subscription" ], "x-mint": { "href": "/api-reference/subscription/get-subscription" }, "summary": "Get Subscription", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SubscriptionResponse" }, "example": { "plan": "lite", "limits": { "concurrency_limit": 5, "custom_voice_slot": 10 }, "credits": { "plan_credits": 200000, "used_credits": 157300 } } } }, "description": "Successful Response" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Unauthorized - Authentication failed" }, "429": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Too many requests" } } }, "description": "Too Many Requests - Rate limit exceeded" }, "500": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "An unexpected error occurred" } } }, "description": "Internal Server Error" } }, "description": "Retrieve the authenticated user's current subscription information, including plan name, credit usage, concurrency limit, and custom voice slot capacity.\n\nUse this endpoint to check remaining credits, verify your current plan, or see how many custom voice slots are available before cloning a new voice with `POST /v1/voices/clone`. The `limits.custom_voice_slot` value is the maximum number of custom voices the current plan can hold at once; free the slot by calling `DELETE /v1/voices/{voice_id}`.", "operationId": "get_my_subscription_v1_users_me_subscription_get" } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "Maximum number of concurrent requests allowed" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "Maximum number of custom voices (created via instant cloning) allowed on the current plan." }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "Usage limit information" }, "Prompt": { "title": "Prompt (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "Emotion preset to apply.\r\n\r\nSupported emotions for ssfm-v21: normal, happy, sad, angry\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "example": 1, "description": "Controls the strength of emotional expression (0.0 to 2.0).\r\n\r\n- 0.0: Completely neutral\r\n- 1.0: Standard expression (default)\r\n- 2.0: Maximum intensity\r\n" } }, "description": "Emotion and style settings for the generated speech." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "Age group classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **child**: Child voice (under 12 years old)\n- **teenager**: Teenage voice (13-19 years old)\n- **young_adult**: Young adult voice (20-35 years old)\n- **middle_age**: Middle-aged voice (36-60 years old)\n- **elder**: Elder voice (over 60 years old)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "Total monthly credits provided by the plan" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "Number of credits used" } }, "description": "Credit usage information" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group classification (child/teenager/young_adult/middle_age/elder)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender classification (male/female)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "List of supported TTS models with their available emotions (e.g., [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix (e.g., `tc_60e5426de8b95f1d3000d7b5`); cloned custom voices created via `POST /v1/voices/clone` use the `uc_` prefix and are also returned by `/v2/voices` for the owner." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "List of use case categories this voice is suitable for" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "Human-readable name of the voice" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type — `original` for Typecast-provided stock voices, `custom` for user-cloned voices." } }, "description": "V2 Voice response model with model-grouped emotions and enhanced metadata" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender (`male` or `female`)." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "Supported TTS models and their available emotions." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix and custom voices use the `uc_` prefix." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "Recommended use-case categories." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "Localized voice names, such as `{\"eng\": \"Daejin\", \"kor\": \"대진\"}`." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type (`original` or `custom`)." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "Preview audio URL when available." } }, "description": "Voice metadata with localized names and a preview audio URL." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "Subscription plan for the API.\n\nAvailable values:\n- **free**: Free tier with limited credits\n- **lite**: Lite plan with moderate credits\n- **plus**: Plus plan with higher credits\n- **custom**: Custom enterprise plan" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "TTS model version to use for speech synthesis. Different models offer varying capabilities and quality levels.\n\nAvailable models:\n- **ssfm-v30**: Latest model with improved prosody and additional emotion presets (recommended)\n- **ssfm-v21**: Stable production model with proven reliability and consistent quality\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS model version (e.g., ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "List of supported emotions for this model" } }, "description": "Model information including version and supported emotions" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "English voice name." }, "kor": { "type": "string", "title": "Kor", "description": "Korean voice name." } }, "description": "Localized voice names keyed by ISO 639-3 language code." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "Voice type classification.\n\n- `original` — Typecast-provided stock voices available to every account.\n- `custom` — Voices the user created by uploading or cloning their own sample." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "Gender classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **male**: Male voice\n- **female**: Female voice\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "Available emotion presets for speech synthesis. Each emotion affects the tone, pace, and expressiveness of the generated speech.\n\n**ssfm-v21 Supported Emotions (4 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n\n**ssfm-v30 Supported Emotions (7 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n- whisper: Soft, quiet speech\n- toneup: Higher tonal emphasis\n- tonedown: Lower tonal emphasis\n\nCheck available emotions for each voice through the /v2/voices API response.\n" }, "SmartPrompt": { "type": "object", "title": "SmartPrompt (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "Text that comes AFTER the `text` field in TTSRequest. Provides forward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model anticipate emotional transitions\r\n- Leave empty if no following context is available\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "Discriminator field to identify the prompt type. Must be set to \"smart\" for context-aware emotion inference.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "Text that comes BEFORE the `text` field in TTSRequest. Provides backward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model understand emotional build-up and context\r\n- Leave empty if no preceding context is available\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "PresetPrompt (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "Discriminator field to identify the prompt type. Must be set to \"preset\" for preset-based emotion control.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "Emotion preset to apply to the generated speech.\r\n\r\nSupported emotions: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "Controls the strength of emotional expression in the generated speech.\r\n\r\n- 0.0: Completely neutral, no emotional coloring\r\n- 0.5: Subtle emotional hints\r\n- 1.0: Standard emotional expression (default)\r\n- 1.5: Strong emotional emphasis\r\n- 2.0: Maximum intensity, highly expressive\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "Voice use case categories for content type filtering. Each voice is tagged with one or more use cases indicating its suitability for specific content types.\n\n**Available Use Cases:**\n- **Announcer**: Public announcements and presentations\n- **Anime**: Animation and character voices\n- **Audiobook**: Long-form narration and storytelling\n- **Conversational**: Chatbots and conversational AI\n- **Documentary**: Documentary narration and commentary\n- **E-learning**: Educational content and tutorials\n- **Rapper**: Rap and music performance\n- **Game**: Video game characters and narration\n- **Tiktok/Reels**: Short-form social media content\n- **News**: News broadcasting\n- **Podcast**: Broadcasting and podcast production\n- **Voicemail**: IVR systems and voice assistants\n- **Ads**: Advertising and promotional content\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "Error message describing the issue" }, "message": { "type": "string", "description": "Human-readable message for structured errors" }, "error_code": { "type": "string", "description": "Machine-readable error code for structured errors" } }, "description": "API errors use `detail` for legacy and validation errors, or `error_code` and `message` for structured errors." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "Failure reason when `status` is `failed`." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "source": { "type": "string", "title": "Source", "description": "Creation method (`instant` or `professional`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "Creation time in UTC." } }, "description": "A custom voice owned by the authenticated account." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "Recommendation relevance score. Higher values indicate a stronger match for the query." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "Typecast voice identifier with the `tc_` prefix. Use this value as `voice_id` in text-to-speech requests." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "Human-readable voice name." } }, "description": "Recommended voice candidate returned by `GET /v1/voices/recommendations`, sorted by relevance." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "Current creation or training status of a custom voice." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Human-readable voice name (1-30 characters)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "Engine model the voice was cloned for (`ssfm-v21` or `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "Custom voice identifier with the `uc_` prefix. Use this value as `voice_id` in `POST /v1/text-to-speech` and other endpoints that accept `voice_id`." } }, "description": "Response of `POST /v1/voices/clone` — custom voice metadata returned by instant cloning." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "Segment discriminator. Always `pause`." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "Pause duration in seconds. Each pause may be up to 10 seconds; all pauses combined may be up to 60 seconds.", "exclusiveMinimum": 0 } }, "description": "A silent interval inserted without consuming credits.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single word-level alignment segment between the original transcript and the generated audio." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "Current subscription plan" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "Usage limit information" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "Credit usage information" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "Subscription information response model" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation and whitespace)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single character-level alignment segment between the original transcript and the generated audio." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." } }, "description": "Result returned after creating an instant or professional custom voice." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "Base64-encoded audio bytes. Decode and write to a file using the `audio_format` extension." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "Word-level timestamps (with attached punctuation). `null` when the request uses `granularity=char`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "Character-level timestamps (including punctuation and whitespace). `null` when the request uses `granularity=word`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "Audio encoding format of the bytes in `audio` — either `wav` or `mp3`, mirroring the request's `output.audio_format`." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "Length of the generated audio in seconds." } }, "description": "Response payload for POST /v1/text-to-speech/with-timestamps — base64-encoded audio plus per-word and per-character timestamps aligned with the generated speech." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "Audio sample. WAV or MP3, max 25 MB, 5-150 seconds.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "Voice name (1-30 characters)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "Engine model the voice is cloned for (`ssfm-v21` or `ssfm-v30`)." } }, "description": "Multipart request body for instant cloning." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "One WAV or MP3 recording, 25 MiB or less and 5 to 150 seconds long.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "Product Narrator", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." } }, "description": "Multipart request body for instant voice cloning." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "Custom Voice Name", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "Files", "description": "WAV or MP3 recordings used for training. Only one file can be uploaded.\r\n\r\n**Upload limits**\r\n\r\n* One WAV or MP3 file\r\n* File size: 1 GiB or less\r\n* Duration: 5 minutes to 3 hours\r\n* Sample rate: 16 kHz or higher\r\n\r\nFor best results, we recommend audio that meets the following conditions:\r\n\r\n* Record in a speaking style that closely matches how you want the generated voice to sound.\r\n* Record in a quiet environment without background noise.\r\n* Include only one speaker.\r\n* Record in the language specified in the `language` field.\r\n* Longer input audio results in higher-quality generated voices." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." }, "language": { "type": "string", "title": "Language", "example": "eng", "description": "ISO 639-3 language code, such as `kor` or `eng`." } }, "description": "Multipart request body for professional voice cloning." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "API key for authentication. You can obtain an API key from the Typecast API Console." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # List Voices (v2) > Lists all available voice models with enhanced metadata and filtering capabilities (V2). This endpoint returns an enhanced voice list with model-grouped emotion support and additional metadata including gender, age group, and use cases. Each voice can support multiple models with their respective emotion sets. **Key Features:** - **Model Grouping**: Each voice includes a `models` array showing all supported TTS models and their available emotions - **Enhanced Metadata**: Includes gender (male/female), age group (child/teenager/young_adult/middle_age/elder), and use cases - **Advanced Filtering**: Filter by model, gender, age, and use cases to find voices matching specific requirements **Use Cases:** - Voice selection UI with demographic filters - Finding voices suitable for specific content types (e.g., Ads, Audiobook, eLearning) - Discovering which emotions are available for each model version ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "Production server" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v2/voices": { "get": { "tags": [ "Deprecated" ], "x-mint": { "href": "/api-reference/deprecated/list-voices-v2" }, "summary": "List Voices (v2)", "responses": { "200": { "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VoiceV2" }, "title": "Response Get Voices" }, "example": [ { "age": "young_adult", "gender": "female", "models": [ { "version": "ssfm-v30", "emotions": [ "normal", "happy", "sad", "angry", "whisper", "toneup", "tonedown" ] }, { "version": "ssfm-v21", "emotions": [ "normal", "happy", "sad", "angry" ] } ], "voice_id": "tc_60e5426de8b95f1d3000d7b5", "use_cases": [ "Audiobook", "E-learning", "Ads" ], "voice_name": "Olivia", "voice_type": "original" } ] } }, "description": "Success - Returns list of voice models with enhanced metadata" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Unauthorized - Invalid or missing API key" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid request format" } } }, "description": "Validation Error - Invalid request parameters" } }, "deprecated": true, "parameters": [ { "in": "query", "name": "model", "schema": { "$ref": "#/components/schemas/TTSModel" }, "required": false, "description": "Filter by voice model (ssfm-v21 or ssfm-v30). Returns voices that support the specified model. Optional - if not provided, returns voices for all models." }, { "in": "query", "name": "gender", "schema": { "$ref": "#/components/schemas/GenderEnum" }, "required": false, "description": "Filter by gender (male or female). Returns voices matching the specified gender. Optional - if not provided, returns voices of all genders." }, { "in": "query", "name": "age", "schema": { "$ref": "#/components/schemas/AgeEnum" }, "required": false, "description": "Filter by age group (child, teenager, young_adult, middle_age, elder). Returns voices matching the specified age group. Optional - if not provided, returns voices of all ages." }, { "in": "query", "name": "use_cases", "schema": { "$ref": "#/components/schemas/UseCasesEnum" }, "required": false, "description": "Filter by use case category. Returns voices tagged with the specified use case (TikTok/Reels/Shorts, Game, Audiobook/Storytelling, etc.). Optional - if not provided, returns all voices regardless of use case." }, { "in": "query", "name": "voice_type", "schema": { "$ref": "#/components/schemas/VoiceType" }, "required": false, "description": "Filter by voice type (`original` or `custom`). Optional - if not provided, returns voices of all types." } ], "description": "Lists all available voice models with enhanced metadata and filtering capabilities (V2).\n\nThis endpoint returns an enhanced voice list with model-grouped emotion support and additional metadata including gender, age group, and use cases. Each voice can support multiple models with their respective emotion sets.\n\n**Key Features:**\n- **Model Grouping**: Each voice includes a `models` array showing all supported TTS models and their available emotions\n- **Enhanced Metadata**: Includes gender (male/female), age group (child/teenager/young_adult/middle_age/elder), and use cases\n- **Advanced Filtering**: Filter by model, gender, age, and use cases to find voices matching specific requirements\n\n**Use Cases:**\n- Voice selection UI with demographic filters\n- Finding voices suitable for specific content types (e.g., Ads, Audiobook, eLearning)\n- Discovering which emotions are available for each model version", "operationId": "get_voices_v2_v2_voices_get", "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request GET \\\n --url 'https://api.typecast.ai/v2/voices?model=ssfm-v30&gender=female&age=young_adult' \\\n --header 'X-API-KEY: '\n" }, { "lang": "C#", "label": "C# (HttpClient)", "source": "using System;\nusing System.Net.Http;\nusing System.Threading.Tasks;\n\nvar client = new HttpClient();\nclient.DefaultRequestHeaders.Add(\"X-API-KEY\", \"\");\n\nvar response = await client.GetAsync(\"https://api.typecast.ai/v2/voices?model=ssfm-v30&gender=female&age=young_adult\");\n\nif (response.IsSuccessStatusCode)\n{\n var content = await response.Content.ReadAsStringAsync();\n Console.WriteLine(content);\n}\n" }, { "lang": "Kotlin", "label": "Kotlin (OkHttp)", "source": "import okhttp3.OkHttpClient\nimport okhttp3.Request\n\nval client = OkHttpClient()\n\nval request = Request.Builder()\n .url(\"https://api.typecast.ai/v2/voices?model=ssfm-v30&gender=female&age=young_adult\")\n .addHeader(\"X-API-KEY\", \"\")\n .get()\n .build()\n\nclient.newCall(request).execute().use { response ->\n if (response.isSuccessful) {\n println(response.body?.string())\n }\n}\n" }, { "lang": "C++", "label": "C++ (libcurl)", "source": "#include \n#include \n#include \n\nsize_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {\n ((std::string*)userp)->append((char*)contents, size * nmemb);\n return size * nmemb;\n}\n\nint main() {\n CURL* curl = curl_easy_init();\n if(curl) {\n std::string readBuffer;\n struct curl_slist* headers = NULL;\n\n headers = curl_slist_append(headers, \"X-API-KEY: \");\n\n curl_easy_setopt(curl, CURLOPT_URL, \"https://api.typecast.ai/v2/voices?model=ssfm-v30&gender=female&age=young_adult\");\n curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);\n curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);\n curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);\n\n CURLcode res = curl_easy_perform(curl);\n if(res == CURLE_OK) {\n std::cout << readBuffer << std::endl;\n }\n\n curl_slist_free_all(headers);\n curl_easy_cleanup(curl);\n }\n return 0;\n}\n" }, { "lang": "C", "label": "C (libcurl)", "source": "#include \n#include \n#include \n#include \n\ntypedef struct {\n char* data;\n size_t size;\n} MemoryStruct;\n\nsize_t WriteMemoryCallback(void* contents, size_t size, size_t nmemb, void* userp) {\n size_t realsize = size * nmemb;\n MemoryStruct* mem = (MemoryStruct*)userp;\n\n char* ptr = realloc(mem->data, mem->size + realsize + 1);\n if(!ptr) return 0;\n\n mem->data = ptr;\n memcpy(&(mem->data[mem->size]), contents, realsize);\n mem->size += realsize;\n mem->data[mem->size] = 0;\n\n return realsize;\n}\n\nint main(void) {\n CURL* curl;\n CURLcode res;\n MemoryStruct chunk = {NULL, 0};\n\n curl_global_init(CURL_GLOBAL_ALL);\n curl = curl_easy_init();\n\n if(curl) {\n struct curl_slist* headers = NULL;\n headers = curl_slist_append(headers, \"X-API-KEY: \");\n\n curl_easy_setopt(curl, CURLOPT_URL, \"https://api.typecast.ai/v2/voices?model=ssfm-v30&gender=female&age=young_adult\");\n curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);\n curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);\n curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&chunk);\n\n res = curl_easy_perform(curl);\n\n if(res == CURLE_OK) {\n printf(\"%s\\n\", chunk.data);\n }\n\n curl_slist_free_all(headers);\n curl_easy_cleanup(curl);\n free(chunk.data);\n }\n\n curl_global_cleanup();\n return 0;\n}\n" }, { "lang": "Swift", "label": "Swift (URLSession)", "source": "import Foundation\n\nlet url = URL(string: \"https://api.typecast.ai/v2/voices?model=ssfm-v30&gender=female&age=young_adult\")!\nvar request = URLRequest(url: url)\nrequest.httpMethod = \"GET\"\nrequest.setValue(\"\", forHTTPHeaderField: \"X-API-KEY\")\n\nlet task = URLSession.shared.dataTask(with: request) { data, response, error in\n if let data = data, let jsonString = String(data: data, encoding: .utf8) {\n print(jsonString)\n }\n}\ntask.resume()\n" }, { "lang": "Rust", "label": "Rust (reqwest)", "source": "use reqwest;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box> {\n let client = reqwest::Client::new();\n\n let response = client\n .get(\"https://api.typecast.ai/v2/voices?model=ssfm-v30&gender=female&age=young_adult\")\n .header(\"X-API-KEY\", \"\")\n .send()\n .await?;\n\n if response.status().is_success() {\n let body = response.text().await?;\n println!(\"{}\", body);\n }\n\n Ok(())\n}\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "Maximum number of concurrent requests allowed" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "Maximum number of custom voices (created via instant cloning) allowed on the current plan." }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "Usage limit information" }, "Prompt": { "title": "Prompt (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "Emotion preset to apply.\r\n\r\nSupported emotions for ssfm-v21: normal, happy, sad, angry\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "example": 1, "description": "Controls the strength of emotional expression (0.0 to 2.0).\r\n\r\n- 0.0: Completely neutral\r\n- 1.0: Standard expression (default)\r\n- 2.0: Maximum intensity\r\n" } }, "description": "Emotion and style settings for the generated speech." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "Age group classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **child**: Child voice (under 12 years old)\n- **teenager**: Teenage voice (13-19 years old)\n- **young_adult**: Young adult voice (20-35 years old)\n- **middle_age**: Middle-aged voice (36-60 years old)\n- **elder**: Elder voice (over 60 years old)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "Total monthly credits provided by the plan" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "Number of credits used" } }, "description": "Credit usage information" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group classification (child/teenager/young_adult/middle_age/elder)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender classification (male/female)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "List of supported TTS models with their available emotions (e.g., [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix (e.g., `tc_60e5426de8b95f1d3000d7b5`); cloned custom voices created via `POST /v1/voices/clone` use the `uc_` prefix and are also returned by `/v2/voices` for the owner." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "List of use case categories this voice is suitable for" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "Human-readable name of the voice" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type — `original` for Typecast-provided stock voices, `custom` for user-cloned voices." } }, "description": "V2 Voice response model with model-grouped emotions and enhanced metadata" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender (`male` or `female`)." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "Supported TTS models and their available emotions." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix and custom voices use the `uc_` prefix." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "Recommended use-case categories." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "Localized voice names, such as `{\"eng\": \"Daejin\", \"kor\": \"대진\"}`." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type (`original` or `custom`)." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "Preview audio URL when available." } }, "description": "Voice metadata with localized names and a preview audio URL." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "Subscription plan for the API.\n\nAvailable values:\n- **free**: Free tier with limited credits\n- **lite**: Lite plan with moderate credits\n- **plus**: Plus plan with higher credits\n- **custom**: Custom enterprise plan" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "TTS model version to use for speech synthesis. Different models offer varying capabilities and quality levels.\n\nAvailable models:\n- **ssfm-v30**: Latest model with improved prosody and additional emotion presets (recommended)\n- **ssfm-v21**: Stable production model with proven reliability and consistent quality\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS model version (e.g., ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "List of supported emotions for this model" } }, "description": "Model information including version and supported emotions" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "English voice name." }, "kor": { "type": "string", "title": "Kor", "description": "Korean voice name." } }, "description": "Localized voice names keyed by ISO 639-3 language code." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "Voice type classification.\n\n- `original` — Typecast-provided stock voices available to every account.\n- `custom` — Voices the user created by uploading or cloning their own sample." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "Gender classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **male**: Male voice\n- **female**: Female voice\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "Available emotion presets for speech synthesis. Each emotion affects the tone, pace, and expressiveness of the generated speech.\n\n**ssfm-v21 Supported Emotions (4 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n\n**ssfm-v30 Supported Emotions (7 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n- whisper: Soft, quiet speech\n- toneup: Higher tonal emphasis\n- tonedown: Lower tonal emphasis\n\nCheck available emotions for each voice through the /v2/voices API response.\n" }, "SmartPrompt": { "type": "object", "title": "SmartPrompt (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "Text that comes AFTER the `text` field in TTSRequest. Provides forward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model anticipate emotional transitions\r\n- Leave empty if no following context is available\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "Discriminator field to identify the prompt type. Must be set to \"smart\" for context-aware emotion inference.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "Text that comes BEFORE the `text` field in TTSRequest. Provides backward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model understand emotional build-up and context\r\n- Leave empty if no preceding context is available\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "PresetPrompt (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "Discriminator field to identify the prompt type. Must be set to \"preset\" for preset-based emotion control.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "Emotion preset to apply to the generated speech.\r\n\r\nSupported emotions: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "Controls the strength of emotional expression in the generated speech.\r\n\r\n- 0.0: Completely neutral, no emotional coloring\r\n- 0.5: Subtle emotional hints\r\n- 1.0: Standard emotional expression (default)\r\n- 1.5: Strong emotional emphasis\r\n- 2.0: Maximum intensity, highly expressive\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "Voice use case categories for content type filtering. Each voice is tagged with one or more use cases indicating its suitability for specific content types.\n\n**Available Use Cases:**\n- **Announcer**: Public announcements and presentations\n- **Anime**: Animation and character voices\n- **Audiobook**: Long-form narration and storytelling\n- **Conversational**: Chatbots and conversational AI\n- **Documentary**: Documentary narration and commentary\n- **E-learning**: Educational content and tutorials\n- **Rapper**: Rap and music performance\n- **Game**: Video game characters and narration\n- **Tiktok/Reels**: Short-form social media content\n- **News**: News broadcasting\n- **Podcast**: Broadcasting and podcast production\n- **Voicemail**: IVR systems and voice assistants\n- **Ads**: Advertising and promotional content\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "Error message describing the issue" }, "message": { "type": "string", "description": "Human-readable message for structured errors" }, "error_code": { "type": "string", "description": "Machine-readable error code for structured errors" } }, "description": "API errors use `detail` for legacy and validation errors, or `error_code` and `message` for structured errors." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "Failure reason when `status` is `failed`." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "source": { "type": "string", "title": "Source", "description": "Creation method (`instant` or `professional`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "Creation time in UTC." } }, "description": "A custom voice owned by the authenticated account." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "Recommendation relevance score. Higher values indicate a stronger match for the query." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "Typecast voice identifier with the `tc_` prefix. Use this value as `voice_id` in text-to-speech requests." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "Human-readable voice name." } }, "description": "Recommended voice candidate returned by `GET /v1/voices/recommendations`, sorted by relevance." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "Current creation or training status of a custom voice." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Human-readable voice name (1-30 characters)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "Engine model the voice was cloned for (`ssfm-v21` or `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "Custom voice identifier with the `uc_` prefix. Use this value as `voice_id` in `POST /v1/text-to-speech` and other endpoints that accept `voice_id`." } }, "description": "Response of `POST /v1/voices/clone` — custom voice metadata returned by instant cloning." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "Segment discriminator. Always `pause`." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "Pause duration in seconds. Each pause may be up to 10 seconds; all pauses combined may be up to 60 seconds.", "exclusiveMinimum": 0 } }, "description": "A silent interval inserted without consuming credits.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single word-level alignment segment between the original transcript and the generated audio." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "Current subscription plan" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "Usage limit information" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "Credit usage information" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "Subscription information response model" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation and whitespace)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single character-level alignment segment between the original transcript and the generated audio." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." } }, "description": "Result returned after creating an instant or professional custom voice." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "Base64-encoded audio bytes. Decode and write to a file using the `audio_format` extension." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "Word-level timestamps (with attached punctuation). `null` when the request uses `granularity=char`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "Character-level timestamps (including punctuation and whitespace). `null` when the request uses `granularity=word`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "Audio encoding format of the bytes in `audio` — either `wav` or `mp3`, mirroring the request's `output.audio_format`." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "Length of the generated audio in seconds." } }, "description": "Response payload for POST /v1/text-to-speech/with-timestamps — base64-encoded audio plus per-word and per-character timestamps aligned with the generated speech." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "Audio sample. WAV or MP3, max 25 MB, 5-150 seconds.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "Voice name (1-30 characters)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "Engine model the voice is cloned for (`ssfm-v21` or `ssfm-v30`)." } }, "description": "Multipart request body for instant cloning." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "One WAV or MP3 recording, 25 MiB or less and 5 to 150 seconds long.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "Product Narrator", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." } }, "description": "Multipart request body for instant voice cloning." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "Custom Voice Name", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "Files", "description": "WAV or MP3 recordings used for training. Only one file can be uploaded.\r\n\r\n**Upload limits**\r\n\r\n* One WAV or MP3 file\r\n* File size: 1 GiB or less\r\n* Duration: 5 minutes to 3 hours\r\n* Sample rate: 16 kHz or higher\r\n\r\nFor best results, we recommend audio that meets the following conditions:\r\n\r\n* Record in a speaking style that closely matches how you want the generated voice to sound.\r\n* Record in a quiet environment without background noise.\r\n* Include only one speaker.\r\n* Record in the language specified in the `language` field.\r\n* Longer input audio results in higher-quality generated voices." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." }, "language": { "type": "string", "title": "Language", "example": "eng", "description": "ISO 639-3 language code, such as `kor` or `eng`." } }, "description": "Multipart request body for professional voice cloning." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "API key for authentication. You can obtain an API key from the Typecast API Console." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Get Voice Details (v2) > Retrieves detailed information for a specific voice with enhanced metadata (V2). This endpoint returns the complete information for a single voice, including model-grouped emotion support and metadata such as gender, age group, and use cases. Use this when you need to verify voice details or check available emotions before making a TTS request. **Response includes:** - **voice_id**: Unique voice identifier - **voice_name**: Human-readable voice name - **models**: Array of supported TTS models with their respective emotion sets - **gender**: Voice gender classification (male/female) - **age**: Age group classification (child/teenager/young_adult/middle_age/elder) - **use_cases**: Recommended content categories for this voice **Use Cases:** - Verify voice availability before TTS request - Check supported emotions for a specific voice and model combination - Display voice details in a voice selection UI ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "Production server" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v2/voices/{voice_id}": { "get": { "tags": [ "Deprecated" ], "x-mint": { "href": "/api-reference/deprecated/get-voice-details-v2" }, "summary": "Get Voice Details (v2)", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VoiceV2" }, "example": { "age": "young_adult", "gender": "female", "models": [ { "version": "ssfm-v30", "emotions": [ "normal", "happy", "sad", "angry", "whisper", "toneup", "tonedown" ] }, { "version": "ssfm-v21", "emotions": [ "normal", "happy", "sad", "angry" ] } ], "voice_id": "tc_60e5426de8b95f1d3000d7b5", "use_cases": [ "Audiobook", "E-learning", "Ads" ], "voice_name": "Olivia", "voice_type": "original" } } }, "description": "Success - Returns detailed information for the requested voice" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Unauthorized - Invalid or missing API key" }, "404": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Voice not found" } } }, "description": "Not Found - Requested voice does not exist" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid request format" } } }, "description": "Validation Error - Invalid voice_id format" } }, "deprecated": true, "parameters": [ { "in": "path", "name": "voice_id", "schema": { "type": "string", "title": "Voice Id" }, "required": true } ], "description": "Retrieves detailed information for a specific voice with enhanced metadata (V2).\n\nThis endpoint returns the complete information for a single voice, including model-grouped emotion support and metadata such as gender, age group, and use cases. Use this when you need to verify voice details or check available emotions before making a TTS request.\n\n**Response includes:**\n- **voice_id**: Unique voice identifier\n- **voice_name**: Human-readable voice name\n- **models**: Array of supported TTS models with their respective emotion sets\n- **gender**: Voice gender classification (male/female)\n- **age**: Age group classification (child/teenager/young_adult/middle_age/elder)\n- **use_cases**: Recommended content categories for this voice\n\n**Use Cases:**\n- Verify voice availability before TTS request\n- Check supported emotions for a specific voice and model combination\n- Display voice details in a voice selection UI", "operationId": "get_voice_v2_v2_voices__voice_id__get", "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request GET \\\n --url 'https://api.typecast.ai/v2/voices/tc_60e5426de8b95f1d3000d7b5' \\\n --header 'X-API-KEY: '\n" }, { "lang": "C#", "label": "C# (HttpClient)", "source": "using System;\nusing System.Net.Http;\nusing System.Threading.Tasks;\n\nvar client = new HttpClient();\nclient.DefaultRequestHeaders.Add(\"X-API-KEY\", \"\");\n\nvar voiceId = \"tc_60e5426de8b95f1d3000d7b5\";\nvar response = await client.GetAsync($\"https://api.typecast.ai/v2/voices/{voiceId}\");\n\nif (response.IsSuccessStatusCode)\n{\n var content = await response.Content.ReadAsStringAsync();\n Console.WriteLine(content);\n}\n" }, { "lang": "Kotlin", "label": "Kotlin (OkHttp)", "source": "import okhttp3.OkHttpClient\nimport okhttp3.Request\n\nval client = OkHttpClient()\nval voiceId = \"tc_60e5426de8b95f1d3000d7b5\"\n\nval request = Request.Builder()\n .url(\"https://api.typecast.ai/v2/voices/$voiceId\")\n .addHeader(\"X-API-KEY\", \"\")\n .get()\n .build()\n\nclient.newCall(request).execute().use { response ->\n if (response.isSuccessful) {\n println(response.body?.string())\n }\n}\n" }, { "lang": "C++", "label": "C++ (libcurl)", "source": "#include \n#include \n#include \n\nsize_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {\n ((std::string*)userp)->append((char*)contents, size * nmemb);\n return size * nmemb;\n}\n\nint main() {\n CURL* curl = curl_easy_init();\n if(curl) {\n std::string readBuffer;\n struct curl_slist* headers = NULL;\n\n headers = curl_slist_append(headers, \"X-API-KEY: \");\n\n std::string url = \"https://api.typecast.ai/v2/voices/tc_60e5426de8b95f1d3000d7b5\";\n\n curl_easy_setopt(curl, CURLOPT_URL, url.c_str());\n curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);\n curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);\n curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);\n\n CURLcode res = curl_easy_perform(curl);\n if(res == CURLE_OK) {\n std::cout << readBuffer << std::endl;\n }\n\n curl_slist_free_all(headers);\n curl_easy_cleanup(curl);\n }\n return 0;\n}\n" }, { "lang": "C", "label": "C (libcurl)", "source": "#include \n#include \n#include \n#include \n\ntypedef struct {\n char* data;\n size_t size;\n} MemoryStruct;\n\nsize_t WriteMemoryCallback(void* contents, size_t size, size_t nmemb, void* userp) {\n size_t realsize = size * nmemb;\n MemoryStruct* mem = (MemoryStruct*)userp;\n\n char* ptr = realloc(mem->data, mem->size + realsize + 1);\n if(!ptr) return 0;\n\n mem->data = ptr;\n memcpy(&(mem->data[mem->size]), contents, realsize);\n mem->size += realsize;\n mem->data[mem->size] = 0;\n\n return realsize;\n}\n\nint main(void) {\n CURL* curl;\n CURLcode res;\n MemoryStruct chunk = {NULL, 0};\n\n curl_global_init(CURL_GLOBAL_ALL);\n curl = curl_easy_init();\n\n if(curl) {\n struct curl_slist* headers = NULL;\n headers = curl_slist_append(headers, \"X-API-KEY: \");\n\n const char* url = \"https://api.typecast.ai/v2/voices/tc_60e5426de8b95f1d3000d7b5\";\n\n curl_easy_setopt(curl, CURLOPT_URL, url);\n curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);\n curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);\n curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&chunk);\n\n res = curl_easy_perform(curl);\n\n if(res == CURLE_OK) {\n printf(\"%s\\n\", chunk.data);\n }\n\n curl_slist_free_all(headers);\n curl_easy_cleanup(curl);\n free(chunk.data);\n }\n\n curl_global_cleanup();\n return 0;\n}\n" }, { "lang": "Swift", "label": "Swift (URLSession)", "source": "import Foundation\n\nlet voiceId = \"tc_60e5426de8b95f1d3000d7b5\"\nlet url = URL(string: \"https://api.typecast.ai/v2/voices/\\(voiceId)\")!\nvar request = URLRequest(url: url)\nrequest.httpMethod = \"GET\"\nrequest.setValue(\"\", forHTTPHeaderField: \"X-API-KEY\")\n\nlet task = URLSession.shared.dataTask(with: request) { data, response, error in\n if let data = data, let jsonString = String(data: data, encoding: .utf8) {\n print(jsonString)\n }\n}\ntask.resume()\n" }, { "lang": "Rust", "label": "Rust (reqwest)", "source": "use reqwest;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box> {\n let client = reqwest::Client::new();\n let voice_id = \"tc_60e5426de8b95f1d3000d7b5\";\n\n let url = format!(\"https://api.typecast.ai/v2/voices/{}\", voice_id);\n\n let response = client\n .get(&url)\n .header(\"X-API-KEY\", \"\")\n .send()\n .await?;\n\n if response.status().is_success() {\n let body = response.text().await?;\n println!(\"{}\", body);\n }\n\n Ok(())\n}\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "Maximum number of concurrent requests allowed" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "Maximum number of custom voices (created via instant cloning) allowed on the current plan." }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "Usage limit information" }, "Prompt": { "title": "Prompt (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "Emotion preset to apply.\r\n\r\nSupported emotions for ssfm-v21: normal, happy, sad, angry\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "example": 1, "description": "Controls the strength of emotional expression (0.0 to 2.0).\r\n\r\n- 0.0: Completely neutral\r\n- 1.0: Standard expression (default)\r\n- 2.0: Maximum intensity\r\n" } }, "description": "Emotion and style settings for the generated speech." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "Age group classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **child**: Child voice (under 12 years old)\n- **teenager**: Teenage voice (13-19 years old)\n- **young_adult**: Young adult voice (20-35 years old)\n- **middle_age**: Middle-aged voice (36-60 years old)\n- **elder**: Elder voice (over 60 years old)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "Total monthly credits provided by the plan" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "Number of credits used" } }, "description": "Credit usage information" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group classification (child/teenager/young_adult/middle_age/elder)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender classification (male/female)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "List of supported TTS models with their available emotions (e.g., [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix (e.g., `tc_60e5426de8b95f1d3000d7b5`); cloned custom voices created via `POST /v1/voices/clone` use the `uc_` prefix and are also returned by `/v2/voices` for the owner." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "List of use case categories this voice is suitable for" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "Human-readable name of the voice" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type — `original` for Typecast-provided stock voices, `custom` for user-cloned voices." } }, "description": "V2 Voice response model with model-grouped emotions and enhanced metadata" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender (`male` or `female`)." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "Supported TTS models and their available emotions." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix and custom voices use the `uc_` prefix." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "Recommended use-case categories." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "Localized voice names, such as `{\"eng\": \"Daejin\", \"kor\": \"대진\"}`." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type (`original` or `custom`)." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "Preview audio URL when available." } }, "description": "Voice metadata with localized names and a preview audio URL." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "Subscription plan for the API.\n\nAvailable values:\n- **free**: Free tier with limited credits\n- **lite**: Lite plan with moderate credits\n- **plus**: Plus plan with higher credits\n- **custom**: Custom enterprise plan" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "TTS model version to use for speech synthesis. Different models offer varying capabilities and quality levels.\n\nAvailable models:\n- **ssfm-v30**: Latest model with improved prosody and additional emotion presets (recommended)\n- **ssfm-v21**: Stable production model with proven reliability and consistent quality\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS model version (e.g., ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "List of supported emotions for this model" } }, "description": "Model information including version and supported emotions" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "English voice name." }, "kor": { "type": "string", "title": "Kor", "description": "Korean voice name." } }, "description": "Localized voice names keyed by ISO 639-3 language code." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "Voice type classification.\n\n- `original` — Typecast-provided stock voices available to every account.\n- `custom` — Voices the user created by uploading or cloning their own sample." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "Gender classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **male**: Male voice\n- **female**: Female voice\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "Available emotion presets for speech synthesis. Each emotion affects the tone, pace, and expressiveness of the generated speech.\n\n**ssfm-v21 Supported Emotions (4 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n\n**ssfm-v30 Supported Emotions (7 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n- whisper: Soft, quiet speech\n- toneup: Higher tonal emphasis\n- tonedown: Lower tonal emphasis\n\nCheck available emotions for each voice through the /v2/voices API response.\n" }, "SmartPrompt": { "type": "object", "title": "SmartPrompt (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "Text that comes AFTER the `text` field in TTSRequest. Provides forward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model anticipate emotional transitions\r\n- Leave empty if no following context is available\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "Discriminator field to identify the prompt type. Must be set to \"smart\" for context-aware emotion inference.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "Text that comes BEFORE the `text` field in TTSRequest. Provides backward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model understand emotional build-up and context\r\n- Leave empty if no preceding context is available\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "PresetPrompt (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "Discriminator field to identify the prompt type. Must be set to \"preset\" for preset-based emotion control.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "Emotion preset to apply to the generated speech.\r\n\r\nSupported emotions: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "Controls the strength of emotional expression in the generated speech.\r\n\r\n- 0.0: Completely neutral, no emotional coloring\r\n- 0.5: Subtle emotional hints\r\n- 1.0: Standard emotional expression (default)\r\n- 1.5: Strong emotional emphasis\r\n- 2.0: Maximum intensity, highly expressive\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "Voice use case categories for content type filtering. Each voice is tagged with one or more use cases indicating its suitability for specific content types.\n\n**Available Use Cases:**\n- **Announcer**: Public announcements and presentations\n- **Anime**: Animation and character voices\n- **Audiobook**: Long-form narration and storytelling\n- **Conversational**: Chatbots and conversational AI\n- **Documentary**: Documentary narration and commentary\n- **E-learning**: Educational content and tutorials\n- **Rapper**: Rap and music performance\n- **Game**: Video game characters and narration\n- **Tiktok/Reels**: Short-form social media content\n- **News**: News broadcasting\n- **Podcast**: Broadcasting and podcast production\n- **Voicemail**: IVR systems and voice assistants\n- **Ads**: Advertising and promotional content\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "Error message describing the issue" }, "message": { "type": "string", "description": "Human-readable message for structured errors" }, "error_code": { "type": "string", "description": "Machine-readable error code for structured errors" } }, "description": "API errors use `detail` for legacy and validation errors, or `error_code` and `message` for structured errors." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "Failure reason when `status` is `failed`." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "source": { "type": "string", "title": "Source", "description": "Creation method (`instant` or `professional`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "Creation time in UTC." } }, "description": "A custom voice owned by the authenticated account." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "Recommendation relevance score. Higher values indicate a stronger match for the query." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "Typecast voice identifier with the `tc_` prefix. Use this value as `voice_id` in text-to-speech requests." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "Human-readable voice name." } }, "description": "Recommended voice candidate returned by `GET /v1/voices/recommendations`, sorted by relevance." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "Current creation or training status of a custom voice." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Human-readable voice name (1-30 characters)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "Engine model the voice was cloned for (`ssfm-v21` or `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "Custom voice identifier with the `uc_` prefix. Use this value as `voice_id` in `POST /v1/text-to-speech` and other endpoints that accept `voice_id`." } }, "description": "Response of `POST /v1/voices/clone` — custom voice metadata returned by instant cloning." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "Segment discriminator. Always `pause`." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "Pause duration in seconds. Each pause may be up to 10 seconds; all pauses combined may be up to 60 seconds.", "exclusiveMinimum": 0 } }, "description": "A silent interval inserted without consuming credits.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single word-level alignment segment between the original transcript and the generated audio." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "Current subscription plan" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "Usage limit information" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "Credit usage information" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "Subscription information response model" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation and whitespace)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single character-level alignment segment between the original transcript and the generated audio." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." } }, "description": "Result returned after creating an instant or professional custom voice." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "Base64-encoded audio bytes. Decode and write to a file using the `audio_format` extension." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "Word-level timestamps (with attached punctuation). `null` when the request uses `granularity=char`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "Character-level timestamps (including punctuation and whitespace). `null` when the request uses `granularity=word`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "Audio encoding format of the bytes in `audio` — either `wav` or `mp3`, mirroring the request's `output.audio_format`." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "Length of the generated audio in seconds." } }, "description": "Response payload for POST /v1/text-to-speech/with-timestamps — base64-encoded audio plus per-word and per-character timestamps aligned with the generated speech." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "Audio sample. WAV or MP3, max 25 MB, 5-150 seconds.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "Voice name (1-30 characters)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "Engine model the voice is cloned for (`ssfm-v21` or `ssfm-v30`)." } }, "description": "Multipart request body for instant cloning." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "One WAV or MP3 recording, 25 MiB or less and 5 to 150 seconds long.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "Product Narrator", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." } }, "description": "Multipart request body for instant voice cloning." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "Custom Voice Name", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "Files", "description": "WAV or MP3 recordings used for training. Only one file can be uploaded.\r\n\r\n**Upload limits**\r\n\r\n* One WAV or MP3 file\r\n* File size: 1 GiB or less\r\n* Duration: 5 minutes to 3 hours\r\n* Sample rate: 16 kHz or higher\r\n\r\nFor best results, we recommend audio that meets the following conditions:\r\n\r\n* Record in a speaking style that closely matches how you want the generated voice to sound.\r\n* Record in a quiet environment without background noise.\r\n* Include only one speaker.\r\n* Record in the language specified in the `language` field.\r\n* Longer input audio results in higher-quality generated voices." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." }, "language": { "type": "string", "title": "Language", "example": "eng", "description": "ISO 639-3 language code, such as `kor` or `eng`." } }, "description": "Multipart request body for professional voice cloning." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "API key for authentication. You can obtain an API key from the Typecast API Console." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Instant Cloning (/v1/voices/clone) > Clone a custom voice from a short audio sample and use it like any built-in voice in subsequent text-to-speech calls. Upload a WAV or MP3 file (max 25 MB). The server extracts a speaker embedding and returns a custom voice ID with the `uc_` prefix that can be passed directly to `POST /v1/text-to-speech` (and any other endpoint that accepts a `voice_id`). The original audio is uploaded to S3 in the background after the response is returned. **Limits** - Audio file: max 25 MB. Supported formats: WAV, MP3. - Audio duration: 5 to 150 seconds. - Voice name: 1-30 characters. - Model: `ssfm-v21` or `ssfm-v30`. The cloned voice is bound to this engine model. - Each plan has a maximum number of active custom voices (the `custom_voice_slot`). Use `DELETE /v1/voices/{voice_id}` to free a slot. **Typical flow** 1. `POST /v1/voices/clone` with the sample audio → receive `voice_id` (e.g. `uc_64a1b2...`). 2. `POST /v1/text-to-speech` with `voice_id` set to the cloned ID. 3. `DELETE /v1/voices/{voice_id}` when you no longer need the voice. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "Production server" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/voices/clone": { "post": { "tags": [ "Deprecated" ], "x-mint": { "href": "/api-reference/voices/instant-cloning" }, "summary": "Instant Cloning (/v1/voices/clone)", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomVoiceResponse" }, "example": { "name": "my-voice", "model": "ssfm-v30", "voice_id": "uc_64a1b2c3d4e5f6a7b8c9d0e1" } } }, "description": "Successful Response - Custom voice created" }, "400": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "examples": { "audio_too_long": { "value": { "detail": "Audio duration exceeds maximum" }, "summary": "Audio is longer than 150 seconds" }, "file_too_large": { "value": { "detail": "File size exceeds maximum limit" }, "summary": "File is too large" }, "audio_too_short": { "value": { "detail": "Audio duration is below minimum" }, "summary": "Audio is shorter than 5 seconds" }, "audio_unreadable": { "value": { "detail": "Failed to read audio metadata" }, "summary": "Audio metadata cannot be read" } } } }, "description": "Bad Request - Invalid audio file, file size, duration, or other validation error" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Unauthorized - Invalid or missing API key" }, "403": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Voice cloning is not available on your plan" } } }, "description": "Forbidden - Voice cloning is not available on your plan" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid request format" } } }, "description": "Validation Error - Invalid request parameters" }, "429": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Too many requests" } } }, "description": "Too Many Requests - Rate limit or concurrency limit exceeded" } }, "deprecated": true, "description": "Clone a custom voice from a short audio sample and use it like any built-in voice in subsequent text-to-speech calls.\n\nUpload a WAV or MP3 file (max 25 MB). The server extracts a speaker embedding and returns a custom voice ID with the `uc_` prefix that can be passed directly to `POST /v1/text-to-speech` (and any other endpoint that accepts a `voice_id`). The original audio is uploaded to S3 in the background after the response is returned.\n\n**Limits**\n\n- Audio file: max 25 MB. Supported formats: WAV, MP3.\n- Audio duration: 5 to 150 seconds.\n- Voice name: 1-30 characters.\n- Model: `ssfm-v21` or `ssfm-v30`. The cloned voice is bound to this engine model.\n- Each plan has a maximum number of active custom voices (the `custom_voice_slot`). Use `DELETE /v1/voices/{voice_id}` to free a slot.\n\n**Typical flow**\n\n1. `POST /v1/voices/clone` with the sample audio → receive `voice_id` (e.g. `uc_64a1b2...`).\n2. `POST /v1/text-to-speech` with `voice_id` set to the cloned ID.\n3. `DELETE /v1/voices/{voice_id}` when you no longer need the voice.", "operationId": "create_voice_clone_v1_voices_clone_post", "requestBody": { "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/Body_create_voice_clone_v1_voices_clone_post", "type": "object", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "format": "binary", "description": "Audio sample. WAV or MP3, max 25 MB, 5-150 seconds." }, "name": { "type": "string", "maxLength": 30, "minLength": 1, "description": "Voice name (1-30 characters)." }, "model": { "enum": [ "ssfm-v21", "ssfm-v30" ], "type": "string", "description": "Engine model to clone the voice for." } } } } }, "required": true }, "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request POST \\\n --url 'https://api.typecast.ai/v1/voices/clone' \\\n --header 'X-API-KEY: ' \\\n -F 'file=@sample.wav' \\\n -F 'name=my-voice' \\\n -F 'model=ssfm-v30'\n" }, { "lang": "Python", "label": "Python (requests)", "source": "import requests\n\nwith open(\"sample.wav\", \"rb\") as f:\n response = requests.post(\n \"https://api.typecast.ai/v1/voices/clone\",\n headers={\"X-API-KEY\": \"\"},\n files={\"file\": (\"sample.wav\", f, \"audio/wav\")},\n data={\"name\": \"my-voice\", \"model\": \"ssfm-v30\"},\n )\n\nprint(response.json())\n# {\"voice_id\": \"uc_64a1b2c3d4e5f6a7b8c9d0e1\", \"name\": \"my-voice\", \"model\": \"ssfm-v30\"}\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "Maximum number of concurrent requests allowed" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "Maximum number of custom voices (created via instant cloning) allowed on the current plan." }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "Usage limit information" }, "Prompt": { "title": "Prompt (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "Emotion preset to apply.\r\n\r\nSupported emotions for ssfm-v21: normal, happy, sad, angry\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "example": 1, "description": "Controls the strength of emotional expression (0.0 to 2.0).\r\n\r\n- 0.0: Completely neutral\r\n- 1.0: Standard expression (default)\r\n- 2.0: Maximum intensity\r\n" } }, "description": "Emotion and style settings for the generated speech." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "Age group classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **child**: Child voice (under 12 years old)\n- **teenager**: Teenage voice (13-19 years old)\n- **young_adult**: Young adult voice (20-35 years old)\n- **middle_age**: Middle-aged voice (36-60 years old)\n- **elder**: Elder voice (over 60 years old)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "Total monthly credits provided by the plan" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "Number of credits used" } }, "description": "Credit usage information" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group classification (child/teenager/young_adult/middle_age/elder)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender classification (male/female)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "List of supported TTS models with their available emotions (e.g., [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix (e.g., `tc_60e5426de8b95f1d3000d7b5`); cloned custom voices created via `POST /v1/voices/clone` use the `uc_` prefix and are also returned by `/v2/voices` for the owner." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "List of use case categories this voice is suitable for" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "Human-readable name of the voice" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type — `original` for Typecast-provided stock voices, `custom` for user-cloned voices." } }, "description": "V2 Voice response model with model-grouped emotions and enhanced metadata" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender (`male` or `female`)." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "Supported TTS models and their available emotions." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix and custom voices use the `uc_` prefix." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "Recommended use-case categories." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "Localized voice names, such as `{\"eng\": \"Daejin\", \"kor\": \"대진\"}`." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type (`original` or `custom`)." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "Preview audio URL when available." } }, "description": "Voice metadata with localized names and a preview audio URL." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "Subscription plan for the API.\n\nAvailable values:\n- **free**: Free tier with limited credits\n- **lite**: Lite plan with moderate credits\n- **plus**: Plus plan with higher credits\n- **custom**: Custom enterprise plan" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "TTS model version to use for speech synthesis. Different models offer varying capabilities and quality levels.\n\nAvailable models:\n- **ssfm-v30**: Latest model with improved prosody and additional emotion presets (recommended)\n- **ssfm-v21**: Stable production model with proven reliability and consistent quality\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS model version (e.g., ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "List of supported emotions for this model" } }, "description": "Model information including version and supported emotions" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "English voice name." }, "kor": { "type": "string", "title": "Kor", "description": "Korean voice name." } }, "description": "Localized voice names keyed by ISO 639-3 language code." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "Voice type classification.\n\n- `original` — Typecast-provided stock voices available to every account.\n- `custom` — Voices the user created by uploading or cloning their own sample." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "Gender classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **male**: Male voice\n- **female**: Female voice\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "Available emotion presets for speech synthesis. Each emotion affects the tone, pace, and expressiveness of the generated speech.\n\n**ssfm-v21 Supported Emotions (4 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n\n**ssfm-v30 Supported Emotions (7 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n- whisper: Soft, quiet speech\n- toneup: Higher tonal emphasis\n- tonedown: Lower tonal emphasis\n\nCheck available emotions for each voice through the /v2/voices API response.\n" }, "SmartPrompt": { "type": "object", "title": "SmartPrompt (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "Text that comes AFTER the `text` field in TTSRequest. Provides forward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model anticipate emotional transitions\r\n- Leave empty if no following context is available\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "Discriminator field to identify the prompt type. Must be set to \"smart\" for context-aware emotion inference.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "Text that comes BEFORE the `text` field in TTSRequest. Provides backward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model understand emotional build-up and context\r\n- Leave empty if no preceding context is available\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "PresetPrompt (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "Discriminator field to identify the prompt type. Must be set to \"preset\" for preset-based emotion control.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "Emotion preset to apply to the generated speech.\r\n\r\nSupported emotions: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "Controls the strength of emotional expression in the generated speech.\r\n\r\n- 0.0: Completely neutral, no emotional coloring\r\n- 0.5: Subtle emotional hints\r\n- 1.0: Standard emotional expression (default)\r\n- 1.5: Strong emotional emphasis\r\n- 2.0: Maximum intensity, highly expressive\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "Voice use case categories for content type filtering. Each voice is tagged with one or more use cases indicating its suitability for specific content types.\n\n**Available Use Cases:**\n- **Announcer**: Public announcements and presentations\n- **Anime**: Animation and character voices\n- **Audiobook**: Long-form narration and storytelling\n- **Conversational**: Chatbots and conversational AI\n- **Documentary**: Documentary narration and commentary\n- **E-learning**: Educational content and tutorials\n- **Rapper**: Rap and music performance\n- **Game**: Video game characters and narration\n- **Tiktok/Reels**: Short-form social media content\n- **News**: News broadcasting\n- **Podcast**: Broadcasting and podcast production\n- **Voicemail**: IVR systems and voice assistants\n- **Ads**: Advertising and promotional content\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "Error message describing the issue" }, "message": { "type": "string", "description": "Human-readable message for structured errors" }, "error_code": { "type": "string", "description": "Machine-readable error code for structured errors" } }, "description": "API errors use `detail` for legacy and validation errors, or `error_code` and `message` for structured errors." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "Failure reason when `status` is `failed`." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "source": { "type": "string", "title": "Source", "description": "Creation method (`instant` or `professional`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "Creation time in UTC." } }, "description": "A custom voice owned by the authenticated account." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "Recommendation relevance score. Higher values indicate a stronger match for the query." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "Typecast voice identifier with the `tc_` prefix. Use this value as `voice_id` in text-to-speech requests." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "Human-readable voice name." } }, "description": "Recommended voice candidate returned by `GET /v1/voices/recommendations`, sorted by relevance." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "Current creation or training status of a custom voice." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Human-readable voice name (1-30 characters)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "Engine model the voice was cloned for (`ssfm-v21` or `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "Custom voice identifier with the `uc_` prefix. Use this value as `voice_id` in `POST /v1/text-to-speech` and other endpoints that accept `voice_id`." } }, "description": "Response of `POST /v1/voices/clone` — custom voice metadata returned by instant cloning." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "Segment discriminator. Always `pause`." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "Pause duration in seconds. Each pause may be up to 10 seconds; all pauses combined may be up to 60 seconds.", "exclusiveMinimum": 0 } }, "description": "A silent interval inserted without consuming credits.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single word-level alignment segment between the original transcript and the generated audio." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "Current subscription plan" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "Usage limit information" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "Credit usage information" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "Subscription information response model" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation and whitespace)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single character-level alignment segment between the original transcript and the generated audio." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." } }, "description": "Result returned after creating an instant or professional custom voice." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "Base64-encoded audio bytes. Decode and write to a file using the `audio_format` extension." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "Word-level timestamps (with attached punctuation). `null` when the request uses `granularity=char`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "Character-level timestamps (including punctuation and whitespace). `null` when the request uses `granularity=word`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "Audio encoding format of the bytes in `audio` — either `wav` or `mp3`, mirroring the request's `output.audio_format`." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "Length of the generated audio in seconds." } }, "description": "Response payload for POST /v1/text-to-speech/with-timestamps — base64-encoded audio plus per-word and per-character timestamps aligned with the generated speech." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "Audio sample. WAV or MP3, max 25 MB, 5-150 seconds.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "Voice name (1-30 characters)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "Engine model the voice is cloned for (`ssfm-v21` or `ssfm-v30`)." } }, "description": "Multipart request body for instant cloning." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "One WAV or MP3 recording, 25 MiB or less and 5 to 150 seconds long.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "Product Narrator", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." } }, "description": "Multipart request body for instant voice cloning." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "Custom Voice Name", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "Files", "description": "WAV or MP3 recordings used for training. Only one file can be uploaded.\r\n\r\n**Upload limits**\r\n\r\n* One WAV or MP3 file\r\n* File size: 1 GiB or less\r\n* Duration: 5 minutes to 3 hours\r\n* Sample rate: 16 kHz or higher\r\n\r\nFor best results, we recommend audio that meets the following conditions:\r\n\r\n* Record in a speaking style that closely matches how you want the generated voice to sound.\r\n* Record in a quiet environment without background noise.\r\n* Include only one speaker.\r\n* Record in the language specified in the `language` field.\r\n* Longer input audio results in higher-quality generated voices." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." }, "language": { "type": "string", "title": "Language", "example": "eng", "description": "ISO 639-3 language code, such as `kor` or `eng`." } }, "description": "Multipart request body for professional voice cloning." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "API key for authentication. You can obtain an API key from the Typecast API Console." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Delete Custom Voice (/v1/voices/{voice_id}) > Soft-delete a custom voice that was created via `POST /v1/voices/clone`. The voice is removed from your active set and the corresponding `custom_voice_slot` becomes available for a new clone. After deletion, the same `voice_id` returns 404 on subsequent requests. Only the owner of the voice may delete it; other users receive 404. Returns 204 No Content on success. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "Production server" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/voices/{voice_id}": { "delete": { "tags": [ "Deprecated" ], "x-mint": { "href": "/api-reference/voices/delete-custom-voice" }, "summary": "Delete Custom Voice (/v1/voices/{voice_id})", "responses": { "204": { "description": "No Content - Voice deleted successfully. The response body is intentionally empty per RFC 9110; treat any 2xx status as success." }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Unauthorized - Invalid or missing API key" }, "404": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Voice not found" } } }, "description": "Not Found - Voice does not exist or is not owned by the caller" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid request format" } } }, "description": "Validation Error - Invalid voice_id format" } }, "deprecated": true, "parameters": [ { "in": "path", "name": "voice_id", "schema": { "type": "string", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "pattern": "^uc_[A-Za-z0-9]+$" }, "required": true, "description": "Custom voice identifier with the `uc_` prefix." } ], "description": "Soft-delete a custom voice that was created via `POST /v1/voices/clone`. The voice is removed from your active set and the corresponding `custom_voice_slot` becomes available for a new clone.\n\nAfter deletion, the same `voice_id` returns 404 on subsequent requests. Only the owner of the voice may delete it; other users receive 404.\n\nReturns 204 No Content on success.", "operationId": "delete_custom_voice_v1_voices__voice_id__delete" } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "Maximum number of concurrent requests allowed" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "Maximum number of custom voices (created via instant cloning) allowed on the current plan." }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "Usage limit information" }, "Prompt": { "title": "Prompt (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "Emotion preset to apply.\r\n\r\nSupported emotions for ssfm-v21: normal, happy, sad, angry\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "example": 1, "description": "Controls the strength of emotional expression (0.0 to 2.0).\r\n\r\n- 0.0: Completely neutral\r\n- 1.0: Standard expression (default)\r\n- 2.0: Maximum intensity\r\n" } }, "description": "Emotion and style settings for the generated speech." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "Age group classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **child**: Child voice (under 12 years old)\n- **teenager**: Teenage voice (13-19 years old)\n- **young_adult**: Young adult voice (20-35 years old)\n- **middle_age**: Middle-aged voice (36-60 years old)\n- **elder**: Elder voice (over 60 years old)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "Total monthly credits provided by the plan" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "Number of credits used" } }, "description": "Credit usage information" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group classification (child/teenager/young_adult/middle_age/elder)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender classification (male/female)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "List of supported TTS models with their available emotions (e.g., [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix (e.g., `tc_60e5426de8b95f1d3000d7b5`); cloned custom voices created via `POST /v1/voices/clone` use the `uc_` prefix and are also returned by `/v2/voices` for the owner." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "List of use case categories this voice is suitable for" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "Human-readable name of the voice" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type — `original` for Typecast-provided stock voices, `custom` for user-cloned voices." } }, "description": "V2 Voice response model with model-grouped emotions and enhanced metadata" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "Voice age group." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "Voice gender (`male` or `female`)." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "Supported TTS models and their available emotions." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique voice identifier. Built-in voices use the `tc_` prefix and custom voices use the `uc_` prefix." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "Recommended use-case categories." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "Localized voice names, such as `{\"eng\": \"Daejin\", \"kor\": \"대진\"}`." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "Voice type (`original` or `custom`)." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "Preview audio URL when available." } }, "description": "Voice metadata with localized names and a preview audio URL." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "Subscription plan for the API.\n\nAvailable values:\n- **free**: Free tier with limited credits\n- **lite**: Lite plan with moderate credits\n- **plus**: Plus plan with higher credits\n- **custom**: Custom enterprise plan" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "TTS model version to use for speech synthesis. Different models offer varying capabilities and quality levels.\n\nAvailable models:\n- **ssfm-v30**: Latest model with improved prosody and additional emotion presets (recommended)\n- **ssfm-v21**: Stable production model with proven reliability and consistent quality\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS model version (e.g., ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "List of supported emotions for this model" } }, "description": "Model information including version and supported emotions" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "English voice name." }, "kor": { "type": "string", "title": "Kor", "description": "Korean voice name." } }, "description": "Localized voice names keyed by ISO 639-3 language code." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "Voice type classification.\n\n- `original` — Typecast-provided stock voices available to every account.\n- `custom` — Voices the user created by uploading or cloning their own sample." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "Gender classification enum - Converts database values (Korean) to API values (English).\n\nAvailable values:\n- **male**: Male voice\n- **female**: Female voice\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "Available emotion presets for speech synthesis. Each emotion affects the tone, pace, and expressiveness of the generated speech.\n\n**ssfm-v21 Supported Emotions (4 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n\n**ssfm-v30 Supported Emotions (7 types):**\n- normal: Neutral, balanced tone\n- happy: Bright, cheerful expression\n- sad: Melancholic, subdued tone\n- angry: Strong, intense delivery\n- whisper: Soft, quiet speech\n- toneup: Higher tonal emphasis\n- tonedown: Lower tonal emphasis\n\nCheck available emotions for each voice through the /v2/voices API response.\n" }, "SmartPrompt": { "type": "object", "title": "SmartPrompt (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "Text that comes AFTER the `text` field in TTSRequest. Provides forward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model anticipate emotional transitions\r\n- Leave empty if no following context is available\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "Discriminator field to identify the prompt type. Must be set to \"smart\" for context-aware emotion inference.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "Text that comes BEFORE the `text` field in TTSRequest. Provides backward context for emotion inference.\r\n\r\nThe model analyzes the flow: `previous_text` → `text` (synthesized) → `next_text`\r\n\r\n- Maximum 2000 characters\r\n- Helps the model understand emotional build-up and context\r\n- Leave empty if no preceding context is available\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "PresetPrompt (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "Discriminator field to identify the prompt type. Must be set to \"preset\" for preset-based emotion control.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "Emotion preset to apply to the generated speech.\r\n\r\nSupported emotions: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\nCheck available emotions for each voice through the /v2/voices API.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "Controls the strength of emotional expression in the generated speech.\r\n\r\n- 0.0: Completely neutral, no emotional coloring\r\n- 0.5: Subtle emotional hints\r\n- 1.0: Standard emotional expression (default)\r\n- 1.5: Strong emotional emphasis\r\n- 2.0: Maximum intensity, highly expressive\r\n" } }, "description": "Emotion and style settings for the generated speech.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "Voice use case categories for content type filtering. Each voice is tagged with one or more use cases indicating its suitability for specific content types.\n\n**Available Use Cases:**\n- **Announcer**: Public announcements and presentations\n- **Anime**: Animation and character voices\n- **Audiobook**: Long-form narration and storytelling\n- **Conversational**: Chatbots and conversational AI\n- **Documentary**: Documentary narration and commentary\n- **E-learning**: Educational content and tutorials\n- **Rapper**: Rap and music performance\n- **Game**: Video game characters and narration\n- **Tiktok/Reels**: Short-form social media content\n- **News**: News broadcasting\n- **Podcast**: Broadcasting and podcast production\n- **Voicemail**: IVR systems and voice assistants\n- **Ads**: Advertising and promotional content\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "Error message describing the issue" }, "message": { "type": "string", "description": "Human-readable message for structured errors" }, "error_code": { "type": "string", "description": "Machine-readable error code for structured errors" } }, "description": "API errors use `detail` for legacy and validation errors, or `error_code` and `message` for structured errors." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "Failure reason when `status` is `failed`." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "source": { "type": "string", "title": "Source", "description": "Creation method (`instant` or `professional`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "Creation time in UTC." } }, "description": "A custom voice owned by the authenticated account." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "Recommendation relevance score. Higher values indicate a stronger match for the query." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "Typecast voice identifier with the `tc_` prefix. Use this value as `voice_id` in text-to-speech requests." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "Human-readable voice name." } }, "description": "Recommended voice candidate returned by `GET /v1/voices/recommendations`, sorted by relevance." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "Current creation or training status of a custom voice." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Human-readable voice name (1-30 characters)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "Engine model the voice was cloned for (`ssfm-v21` or `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "Custom voice identifier with the `uc_` prefix. Use this value as `voice_id` in `POST /v1/text-to-speech` and other endpoints that accept `voice_id`." } }, "description": "Response of `POST /v1/voices/clone` — custom voice metadata returned by instant cloning." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "Segment discriminator. Always `pause`." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "Pause duration in seconds. Each pause may be up to 10 seconds; all pauses combined may be up to 60 seconds.", "exclusiveMinimum": 0 } }, "description": "A silent interval inserted without consuming credits.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single word-level alignment segment between the original transcript and the generated audio." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "Current subscription plan" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "Usage limit information" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "Credit usage information" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "Subscription information response model" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "End time of this segment, in seconds from the beginning of the audio." }, "text": { "type": "string", "title": "Text", "description": "The text fragment from the original transcript (includes any attached punctuation and whitespace)." }, "start": { "type": "number", "title": "Start", "description": "Start time of this segment, in seconds from the beginning of the audio." } }, "description": "A single character-level alignment segment between the original transcript and the generated audio." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "Voice name." }, "model": { "type": "string", "title": "Model", "description": "TTS model version." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "Current creation or training status." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "Unique custom voice identifier with the `uc_` prefix." } }, "description": "Result returned after creating an instant or professional custom voice." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "Base64-encoded audio bytes. Decode and write to a file using the `audio_format` extension." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "Word-level timestamps (with attached punctuation). `null` when the request uses `granularity=char`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "Character-level timestamps (including punctuation and whitespace). `null` when the request uses `granularity=word`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "Audio encoding format of the bytes in `audio` — either `wav` or `mp3`, mirroring the request's `output.audio_format`." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "Length of the generated audio in seconds." } }, "description": "Response payload for POST /v1/text-to-speech/with-timestamps — base64-encoded audio plus per-word and per-character timestamps aligned with the generated speech." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "Audio sample. WAV or MP3, max 25 MB, 5-150 seconds.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "Voice name (1-30 characters)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "Engine model the voice is cloned for (`ssfm-v21` or `ssfm-v30`)." } }, "description": "Multipart request body for instant cloning." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "One WAV or MP3 recording, 25 MiB or less and 5 to 150 seconds long.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "Product Narrator", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." } }, "description": "Multipart request body for instant voice cloning." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "Custom Voice Name", "maxLength": 30, "minLength": 1, "description": "Voice name, up to 30 characters." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "Files", "description": "WAV or MP3 recordings used for training. Only one file can be uploaded.\r\n\r\n**Upload limits**\r\n\r\n* One WAV or MP3 file\r\n* File size: 1 GiB or less\r\n* Duration: 5 minutes to 3 hours\r\n* Sample rate: 16 kHz or higher\r\n\r\nFor best results, we recommend audio that meets the following conditions:\r\n\r\n* Record in a speaking style that closely matches how you want the generated voice to sound.\r\n* Record in a quiet environment without background noise.\r\n* Include only one speaker.\r\n* Record in the language specified in the `language` field.\r\n* Longer input audio results in higher-quality generated voices." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS model version." }, "language": { "type": "string", "title": "Language", "example": "eng", "description": "ISO 639-3 language code, such as `kor` or `eng`." } }, "description": "Multipart request body for professional voice cloning." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "API key for authentication. You can obtain an API key from the Typecast API Console." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 텍스트 음성 변환(TTS) > 보이스를 설정하여 텍스트에서 음성을 생성합니다. 감정, 볼륨, 피치, 템포 맞춤 설정을 지원합니다. 먼저 GET /v3/voices 엔드포인트를 사용하여 사용 가능한 모든 보이스를 조회한 다음, 응답의 voice\_id를 사용하여 이 엔드포인트로 음성을 생성합니다. 각 보이스에는 고유한 특성이 있습니다. 사용 가능한 보이스는 [보이스 목록 조회](/docs/ko/api-reference/voices/list-voices)를 참조하세요. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "프로덕션 서버" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/text-to-speech": { "post": { "tags": [ "Text-to-Speech" ], "x-mint": { "href": "/ko/api-reference/text-to-speech/text-to-speech" }, "summary": "텍스트 음성 변환(TTS)", "security": [ { "ApiKeyAuth": [] } ], "responses": { "200": { "content": { "audio/wav": { "schema": { "type": "string", "format": "binary", "description": "WAV 오디오 파일 바이너리 데이터(비압축 PCM, 16비트, 모노, 44.1kHz)" }, "example": "[Binary audio data - WAV file content]" } }, "description": "Success - Returns audio file" }, "400": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid voice_id" } } }, "description": "Bad Request - Invalid parameters" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Unauthorized - Authentication failed" }, "402": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Insufficient credit" } } }, "description": "Payment Required - Insufficient credits" }, "404": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Voice not found" } } }, "description": "Not Found - Voice model not available" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" } } }, "description": "유효성 검사 오류 - 요청이 올바르지 않거나 입력 텍스트를 합성할 수 없는 경우" }, "429": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Too many requests" } } }, "description": "Too Many Requests - Rate limit exceeded" }, "500": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "An unexpected error occurred" } } }, "description": "Internal Server Error - Server processing failed" } }, "deprecated": false, "description": "보이스를 설정하여 텍스트에서 음성을 생성합니다. 감정, 볼륨, 피치, 템포 맞춤 설정을 지원합니다.\n\n먼저 GET /v3/voices 엔드포인트를 사용하여 사용 가능한 모든 보이스를 조회한 다음, 응답의 voice\\_id를 사용하여 이 엔드포인트로 음성을 생성합니다. 각 보이스에는 고유한 특성이 있습니다. 사용 가능한 보이스는 [보이스 목록 조회](/docs/ko/api-reference/voices/list-voices)를 참조하세요.", "operationId": "text_to_speech_v1_text_to_speech_post", "requestBody": { "content": { "application/json": { "schema": { "type": "object", "title": "TTSRequest", "required": [ "text", "model", "voice_id" ], "properties": { "seed": { "type": "integer", "anyOf": [ { "type": "integer", "maximum": 4294967295, "minimum": 0 }, { "type": "null" } ], "title": "Seed", "format": "uint32", "example": 42, "minimum": 0, "description": "재현 가능한 음성 생성을 위한 부호 없는 정수 시드. 동일한 시드와 동일한 입력 파라미터로 항상 같은 오디오 결과를 생성합니다.\r\n\r\n* 0 이상의 정수만 허용됩니다. 음수 값은 사용할 수 없습니다.\r\n* 생략하면 서버가 매번 랜덤 시드를 생성하여 약간의 변이가 발생합니다." }, "text": { "type": "string", "title": "Text", "example": "모든 것이 너무나 완벽해서 마치 꿈을 꾸는 것 같습니다.", "maxLength": 2000, "minLength": 1, "description": "음성으로 변환할 텍스트. 최소 1자, 최대 2000자. 텍스트 길이에 따라 크레딧이 소비됩니다. 영어, 한국어, 일본어, 중국어를 포함한 여러 언어를 지원합니다. 특수 문자와 구두점은 자동으로 처리됩니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "음성 합성에 사용할 보이스 모델.\r\n\r\n* **ssfm-v30**: 향상된 플로우와 추가 감정 프리셋이 있는 최신 모델(권장)\r\n* **ssfm-v21**: 빠르고 안정적인 모델로 신뢰할 수 있는 품질 제공" }, "output": { "type": "object", "title": "", "properties": { "volume": { "anyOf": [ { "type": "integer", "maximum": 200, "minimum": 0 }, { "type": "null" } ], "title": "Volume", "example": 100, "description": "출력 음성의 상대적인 음량 조절: 0(완전 무음), 50(절반 볼륨), 100(표준 볼륨, 기본값), 150(표준보다 50% 크게), 200(최대 볼륨, 표준의 두 배).\r\n\r\n출력된 음성마다 음량이 다를 경우, 단순 비율 조절인 `volume`을 사용하면 음성 간의 음량 편차가 더욱 커질 수 있습니다. 일정한 음량 출력이 필요한 경우 `target_lufs` 사용을 권장합니다.\r\n\r\n- **주의:** `target_lufs`와 동시에 사용할 수 없습니다.\r\n\r\n필수 범위: 0 <= x <= 200\r\n" }, "audio_pitch": { "type": "integer", "title": "Audio Pitch", "default": 0, "example": 0, "maximum": 12, "minimum": -12, "description": "성별과 나이에 영향을 주는 반음 단위의 피치 조정: -12(한 옥타브 낮게, 더 깊은 목소리), -6(반 옥타브 낮게), 0(원래 피치, 기본값), +6(반 옥타브 높게), +12(한 옥타브 높게, 더 높은 목소리)" }, "audio_tempo": { "type": "number", "title": "Audio Tempo", "default": 1, "example": 1, "maximum": 2, "minimum": 0.5, "description": "음성 속도 제어: 0.5(절반 속도, 매우 느리고 명확함), 0.75(보통보다 약간 느림), 1.0(보통 말하기 속도, 기본값), 1.5(보통보다 50% 빠름), 2.0(두 배 속도, 매우 빠른 음성)" }, "target_lufs": { "type": "integer", "anyOf": [ { "type": "number", "maximum": 0, "minimum": -70 }, { "type": "null" } ], "title": "Target Lufs", "example": -14, "description": "출력 음성의 목표 절대 음량(LUFS) 설정. 원본 음성의 크기와 상관없이 모든 음성을 일정한 크기로 정규화하여 생성합니다. 값이 0에 가까울수록 소리가 커지며, -70에 가까울수록 작아집니다.\r\n\r\n- 필수 범위: -70 <= x <= 0\r\n- 권장값: -14 (일반적인 스트리밍 표준), -23 (방송 표준)\r\n- **주의:** `volume` 파라미터와 함께 사용할 수 없습니다. 절대적인 음량 기준이 필요할 때는 `target_lufs`를, 상대적인 비율 조절이 필요할 때는 `volume`을 선택하여 사용하세요.\r\n" }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "default": "wav", "example": "wav", "description": "출력 오디오 형식.\r\n\r\n**WAV 형식:**\r\n- 비압축 PCM 오디오\r\n- 16비트 깊이, 모노 채널, 44100 Hz 샘플링 속도\r\n- 더 높은 품질, 더 큰 파일 크기\r\n- 전문 오디오 제작에 권장\r\n\r\n**MP3 형식:**\r\n- 압축된 MPEG Layer III 오디오\r\n- 320 kbps 비트레이트, 44100 Hz 샘플링 속도\r\n- 더 작은 파일 크기\r\n- 웹 스트리밍 및 배포에 권장\r\n" }, "remove_silence_ms": { "anyOf": [ { "type": "integer", "maximum": 1000, "minimum": 0 }, { "type": "null" } ], "title": "무음 구간 길이 (ms)", "default": null, "example": 100, "description": "무음 제거를 적용하면 음성 내 무음 구간 중 지정한 길이보다 긴 구간을 해당 길이로 줄입니다. 단위는 밀리초(ms)입니다. 지정값은 제거할 시간이 아니라 남길 무음 길이입니다.\r\n\r\n**입력값:**\r\n- **0부터 1000까지**의 정수, 권장 범위는 0~200.\r\n- 생략 또는 `null`: 무음 제거를 적용하지 않습니다.\r\n- `0`: 0ms 초과의 무음을 제거합니다. **기능을 끄는 값이 아닙니다.**\r\n- 불리언·문자열·소수·범위 밖 값은 허용하지 않습니다.\r\n\r\n**예시:** `100`을 지정하면 음성 내 무음 구간 중 100ms보다 긴 구간을 100ms로 줄입니다. \r\n값이 작을수록 더 짧은 무음 구간까지 제거 대상에 포함되고, 각 구간에서 남기는 무음도 짧아집니다." } }, "description": "볼륨(0-200), 피치(-12\\~+12 반음), 템포(0.5배\\~2.0배), 형식(wav/mp3)을 포함한 오디오 출력 설정으로 최종 오디오 특성을 제어합니다" }, "prompt": { "type": "string", "oneOf": [ { "$ref": "#/components/schemas/SmartPrompt" }, { "$ref": "#/components/schemas/PresetPrompt" }, { "$ref": "#/components/schemas/Prompt" } ], "title": "Prompt", "description": "생성된 음성의 감정 및 스타일 설정, 감정 유형(happy/sad/angry/normal) 및 강도(0.0\\~2.0)를 포함하여 감정 표현을 제어합니다" }, "language": { "type": "string", "title": "Language", "example": "kor", "description": "ISO 639-3 표준을 따르는 언어 코드. 대소문자 구분 안 함(\"KOR\"과 \"kor\" 모두 허용). 제공하지 않으면 텍스트 내용을 기반으로 자동 감지됩니다.\r\n\r\n
\r\n ssfm-v30 지원 언어 (37개)\r\n\r\n | 코드 | 언어 | 코드 | 언어 | 코드 | 언어 |\r\n | --- | ------ | --- | ------ | --- | ------ |\r\n | ARA | 아랍어 | IND | 인도네시아어 | POR | 포르투갈어 |\r\n | BEN | 벵골어 | ITA | 이탈리아어 | RON | 루마니아어 |\r\n | BUL | 불가리아어 | JPN | 일본어 | RUS | 러시아어 |\r\n | CES | 체코어 | KOR | 한국어 | SLK | 슬로바키아어 |\r\n | DAN | 덴마크어 | MSA | 말레이어 | SPA | 스페인어 |\r\n | DEU | 독일어 | NAN | 민남어 | SWE | 스웨덴어 |\r\n | ELL | 그리스어 | NLD | 네덜란드어 | TAM | 타밀어 |\r\n | ENG | 영어 | NOR | 노르웨이어 | TGL | 타갈로그어 |\r\n | FIN | 핀란드어 | PAN | 펀자브어 | THA | 태국어 |\r\n | FRA | 프랑스어 | POL | 폴란드어 | TUR | 터키어 |\r\n | HIN | 힌디어 | UKR | 우크라이나어 | VIE | 베트남어 |\r\n | HRV | 크로아티아어 | YUE | 광둥어 | ZHO | 중국어 |\r\n | HUN | 헝가리어 | | | | |\r\n
\r\n\r\n
\r\n ssfm-v21 지원 언어 (27개)\r\n\r\n | 코드 | 언어 | 코드 | 언어 | 코드 | 언어 |\r\n | --- | ----- | --- | ------ | --- | ------ |\r\n | ARA | 아랍어 | IND | 인도네시아어 | RON | 루마니아어 |\r\n | BUL | 불가리아어 | ITA | 이탈리아어 | RUS | 러시아어 |\r\n | CES | 체코어 | JPN | 일본어 | SLK | 슬로바키아어 |\r\n | DAN | 덴마크어 | KOR | 한국어 | SPA | 스페인어 |\r\n | DEU | 독일어 | MSA | 말레이어 | SWE | 스웨덴어 |\r\n | ELL | 그리스어 | NLD | 네덜란드어 | TAM | 타밀어 |\r\n | ENG | 영어 | POL | 폴란드어 | TGL | 타갈로그어 |\r\n | FIN | 핀란드어 | POR | 포르투갈어 | UKR | 우크라이나어 |\r\n | FRA | 프랑스어 | HRV | 크로아티아어 | ZHO | 중국어 |\r\n
" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "보이스 식별자. 두 가지 prefix 를 지원합니다.\r\n\r\n* `tc_` — 기본 제공되는 타입캐스트 보이스 (예: `tc_60e5426de8b95f1d3000d7b5`). 사용 가능한 ID 는 [보이스 목록 조회](/docs/ko/api-reference/voices/list-voices) 를 참조하세요.\r\n* `uc_` — [퀵 클로닝](/docs/ko/api-reference/voices/instant-cloning) 으로 생성한 커스텀 보이스 (예: `uc_64a1b2c3d4e5f6a7b8c9d0e1`). 본인이 소유한 클로닝 보이스만 사용할 수 있습니다.\r\n\r\n대소문자 구분: prefix 는 소문자만 사용합니다." } } } } }, "required": true, "description": "" }, "x-codeSamples": [ { "lang": "cURL", "label": "cURL (파일로 저장)", "source": "curl --request POST \\\n --url https://api.typecast.ai/v1/text-to-speech \\\n --header 'Content-Type: application/json' \\\n --header 'X-API-KEY: ' \\\n --output output.wav \\\n --data @- <\",\n \"Content-Type\": \"application/json\",\n}\npayload = {\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\": \"Everything is so incredibly perfect that I feel like I'm dreaming.\",\n \"model\": \"ssfm-v30\",\n \"language\": \"eng\",\n \"prompt\": {\n \"emotion_type\": \"smart\",\n \"previous_text\": \"I feel like I'm walking on air and I just want to scream with joy!\",\n \"next_text\": \"I am literally bursting with happiness and I never want this feeling to end!\",\n },\n \"output\": {\n \"volume\": 100,\n \"audio_pitch\": 0,\n \"audio_tempo\": 1,\n \"audio_format\": \"wav\",\n },\n \"seed\": 42,\n}\n\nresponse = requests.post(f\"{API_HOST}/v1/text-to-speech\", headers=headers, json=payload, timeout=60)\nresponse.raise_for_status()\n\nwith open(\"output.wav\", \"wb\") as f:\n f.write(response.content)\nprint(f\"Saved {len(response.content)} bytes to output.wav\")\n" }, { "lang": "C#", "label": "C# (HttpClient)", "source": "using System;\nusing System.Net.Http;\nusing System.Text;\nusing System.Threading.Tasks;\n\nvar client = new HttpClient();\nclient.DefaultRequestHeaders.Add(\"X-API-KEY\", \"\");\n\nvar requestBody = @\"{\n \"\"voice_id\"\": \"\"tc_60e5426de8b95f1d3000d7b5\"\",\n \"\"text\"\": \"\"Everything is so incredibly perfect that I feel like I'm dreaming.\"\",\n \"\"model\"\": \"\"ssfm-v30\"\",\n \"\"language\"\": \"\"eng\"\",\n \"\"prompt\"\": {\n \"\"emotion_type\"\": \"\"smart\"\",\n \"\"previous_text\"\": \"\"I feel like I'm walking on air and I just want to scream with joy!\"\",\n \"\"next_text\"\": \"\"I am literally bursting with happiness and I never want this feeling to end!\"\"\n },\n \"\"output\"\": {\n \"\"volume\"\": 100,\n \"\"audio_pitch\"\": 0,\n \"\"audio_tempo\"\": 1,\n \"\"audio_format\"\": \"\"wav\"\"\n },\n \"\"seed\"\": 42\n}\";\n\nvar content = new StringContent(requestBody, Encoding.UTF8, \"application/json\");\nvar response = await client.PostAsync(\"https://api.typecast.ai/v1/text-to-speech\", content);\n\nif (response.IsSuccessStatusCode)\n{\n var audioBytes = await response.Content.ReadAsByteArrayAsync();\n await File.WriteAllBytesAsync(\"output.wav\", audioBytes);\n Console.WriteLine(\"Audio saved to output.wav\");\n}\n" }, { "lang": "Kotlin", "label": "Kotlin (OkHttp)", "source": "import okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport java.io.File\n\nval client = OkHttpClient()\nval mediaType = \"application/json\".toMediaType()\n\nval requestBody = \"\"\"\n{\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\": \"Everything is so incredibly perfect that I feel like I'm dreaming.\",\n \"model\": \"ssfm-v30\",\n \"language\": \"eng\",\n \"prompt\": {\n \"emotion_type\": \"smart\",\n \"previous_text\": \"I feel like I'm walking on air and I just want to scream with joy!\",\n \"next_text\": \"I am literally bursting with happiness and I never want this feeling to end!\"\n },\n \"output\": {\n \"volume\": 100,\n \"audio_pitch\": 0,\n \"audio_tempo\": 1,\n \"audio_format\": \"wav\"\n },\n \"seed\": 42\n}\n\"\"\".trimIndent()\n\nval request = Request.Builder()\n .url(\"https://api.typecast.ai/v1/text-to-speech\")\n .addHeader(\"X-API-KEY\", \"\")\n .addHeader(\"Content-Type\", \"application/json\")\n .post(requestBody.toRequestBody(mediaType))\n .build()\n\nclient.newCall(request).execute().use { response ->\n if (response.isSuccessful) {\n response.body?.bytes()?.let {\n File(\"output.wav\").writeBytes(it)\n println(\"Audio saved to output.wav\")\n }\n }\n}\n" }, { "lang": "C++", "label": "C++ (libcurl)", "source": "#include \n#include \n#include \n\nsize_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {\n ((std::string*)userp)->append((char*)contents, size * nmemb);\n return size * nmemb;\n}\n\nint main() {\n CURL* curl = curl_easy_init();\n if(curl) {\n std::string readBuffer;\n struct curl_slist* headers = NULL;\n\n headers = curl_slist_append(headers, \"Content-Type: application/json\");\n headers = curl_slist_append(headers, \"X-API-KEY: \");\n\n std::string jsonData = R\"({\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\": \"Everything is so incredibly perfect that I feel like I'm dreaming.\",\n \"model\": \"ssfm-v30\",\n \"language\": \"eng\",\n \"prompt\": {\n \"emotion_type\": \"smart\",\n \"previous_text\": \"I feel like I'm walking on air and I just want to scream with joy!\",\n \"next_text\": \"I am literally bursting with happiness and I never want this feeling to end!\"\n },\n \"output\": {\n \"volume\": 100,\n \"audio_pitch\": 0,\n \"audio_tempo\": 1,\n \"audio_format\": \"wav\"\n },\n \"seed\": 42\n })\";\n\n curl_easy_setopt(curl, CURLOPT_URL, \"https://api.typecast.ai/v1/text-to-speech\");\n curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);\n curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonData.c_str());\n curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);\n curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);\n\n CURLcode res = curl_easy_perform(curl);\n if(res == CURLE_OK) {\n std::ofstream outFile(\"output.wav\", std::ios::binary);\n outFile.write(readBuffer.c_str(), readBuffer.size());\n outFile.close();\n }\n\n curl_slist_free_all(headers);\n curl_easy_cleanup(curl);\n }\n return 0;\n}\n" }, { "lang": "C", "label": "C (libcurl)", "source": "#include \n#include \n#include \n#include \n\ntypedef struct {\n char* data;\n size_t size;\n} MemoryStruct;\n\nsize_t WriteMemoryCallback(void* contents, size_t size, size_t nmemb, void* userp) {\n size_t realsize = size * nmemb;\n MemoryStruct* mem = (MemoryStruct*)userp;\n\n char* ptr = realloc(mem->data, mem->size + realsize + 1);\n if(!ptr) return 0;\n\n mem->data = ptr;\n memcpy(&(mem->data[mem->size]), contents, realsize);\n mem->size += realsize;\n mem->data[mem->size] = 0;\n\n return realsize;\n}\n\nint main(void) {\n CURL* curl;\n CURLcode res;\n MemoryStruct chunk = {NULL, 0};\n\n curl_global_init(CURL_GLOBAL_ALL);\n curl = curl_easy_init();\n\n if(curl) {\n struct curl_slist* headers = NULL;\n headers = curl_slist_append(headers, \"Content-Type: application/json\");\n headers = curl_slist_append(headers, \"X-API-KEY: \");\n\n const char* jsonData = \"{\"\n \"\\\"voice_id\\\":\\\"tc_60e5426de8b95f1d3000d7b5\\\",\"\n \"\\\"text\\\":\\\"Everything is so incredibly perfect that I feel like I'm dreaming.\\\",\"\n \"\\\"model\\\":\\\"ssfm-v30\\\",\"\n \"\\\"language\\\":\\\"eng\\\",\"\n \"\\\"output\\\":{\\\"volume\\\":100,\\\"audio_pitch\\\":0,\\\"audio_tempo\\\":1,\\\"audio_format\\\":\\\"wav\\\"},\"\n \"\\\"seed\\\":42\"\n \"}\";\n\n curl_easy_setopt(curl, CURLOPT_URL, \"https://api.typecast.ai/v1/text-to-speech\");\n curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);\n curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonData);\n curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);\n curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&chunk);\n\n res = curl_easy_perform(curl);\n\n if(res == CURLE_OK) {\n FILE* fp = fopen(\"output.wav\", \"wb\");\n fwrite(chunk.data, 1, chunk.size, fp);\n fclose(fp);\n }\n\n curl_slist_free_all(headers);\n curl_easy_cleanup(curl);\n free(chunk.data);\n }\n\n curl_global_cleanup();\n return 0;\n}\n" }, { "lang": "Swift", "label": "Swift (URLSession)", "source": "import Foundation\n\nlet url = URL(string: \"https://api.typecast.ai/v1/text-to-speech\")!\nvar request = URLRequest(url: url)\nrequest.httpMethod = \"POST\"\nrequest.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\nrequest.setValue(\"\", forHTTPHeaderField: \"X-API-KEY\")\n\nlet requestBody: [String: Any] = [\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\": \"Everything is so incredibly perfect that I feel like I'm dreaming.\",\n \"model\": \"ssfm-v30\",\n \"language\": \"eng\",\n \"prompt\": [\n \"emotion_type\": \"smart\",\n \"previous_text\": \"I feel like I'm walking on air and I just want to scream with joy!\",\n \"next_text\": \"I am literally bursting with happiness and I never want this feeling to end!\"\n ],\n \"output\": [\n \"volume\": 100,\n \"audio_pitch\": 0,\n \"audio_tempo\": 1,\n \"audio_format\": \"wav\"\n ],\n \"seed\": 42\n]\n\nrequest.httpBody = try? JSONSerialization.data(withJSONObject: requestBody)\n\nlet task = URLSession.shared.dataTask(with: request) { data, response, error in\n if let data = data {\n try? data.write(to: URL(fileURLWithPath: \"output.wav\"))\n print(\"Audio saved to output.wav\")\n }\n}\ntask.resume()\n" }, { "lang": "Rust", "label": "Rust (reqwest)", "source": "use reqwest;\nuse serde_json::json;\nuse std::fs::File;\nuse std::io::Write;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box> {\n let client = reqwest::Client::new();\n\n let request_body = json!({\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\": \"Everything is so incredibly perfect that I feel like I'm dreaming.\",\n \"model\": \"ssfm-v30\",\n \"language\": \"eng\",\n \"prompt\": {\n \"emotion_type\": \"smart\",\n \"previous_text\": \"I feel like I'm walking on air and I just want to scream with joy!\",\n \"next_text\": \"I am literally bursting with happiness and I never want this feeling to end!\"\n },\n \"output\": {\n \"volume\": 100,\n \"audio_pitch\": 0,\n \"audio_tempo\": 1,\n \"audio_format\": \"wav\"\n },\n \"seed\": 42\n });\n\n let response = client\n .post(\"https://api.typecast.ai/v1/text-to-speech\")\n .header(\"X-API-KEY\", \"\")\n .header(\"Content-Type\", \"application/json\")\n .json(&request_body)\n .send()\n .await?;\n\n if response.status().is_success() {\n let bytes = response.bytes().await?;\n let mut file = File::create(\"output.wav\")?;\n file.write_all(&bytes)?;\n println!(\"Audio saved to output.wav\");\n }\n\n Ok(())\n}\n" }, { "lang": "JavaScript", "label": "JavaScript (Node.js)", "source": "// Node 18+ (built-in fetch).\nimport { writeFile } from \"node:fs/promises\";\n\nconst response = await fetch(\"https://api.typecast.ai/v1/text-to-speech\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-KEY\": \"\",\n },\n body: JSON.stringify({\n voice_id: \"tc_60e5426de8b95f1d3000d7b5\",\n text: \"Everything is so incredibly perfect that I feel like I'm dreaming.\",\n model: \"ssfm-v30\",\n language: \"eng\",\n prompt: {\n emotion_type: \"smart\",\n previous_text: \"I feel like I'm walking on air and I just want to scream with joy!\",\n next_text: \"I am literally bursting with happiness and I never want this feeling to end!\",\n },\n output: { volume: 100, audio_pitch: 0, audio_tempo: 1, audio_format: \"wav\" },\n seed: 42,\n }),\n});\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\n\nconst buffer = Buffer.from(await response.arrayBuffer());\nawait writeFile(\"output.wav\", buffer);\nconsole.log(`Saved ${buffer.length} bytes to output.wav`);\n" }, { "lang": "PHP", "label": "PHP (curl)", "source": " \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\" => \"Everything is so incredibly perfect that I feel like I'm dreaming.\",\n \"model\" => \"ssfm-v30\",\n \"language\" => \"eng\",\n \"prompt\" => [\n \"emotion_type\" => \"smart\",\n \"previous_text\" => \"I feel like I'm walking on air and I just want to scream with joy!\",\n \"next_text\" => \"I am literally bursting with happiness and I never want this feeling to end!\",\n ],\n \"output\" => [\"volume\" => 100, \"audio_pitch\" => 0, \"audio_tempo\" => 1, \"audio_format\" => \"wav\"],\n \"seed\" => 42,\n]);\n\n$ch = curl_init(\"https://api.typecast.ai/v1/text-to-speech\");\ncurl_setopt_array($ch, [\n CURLOPT_POST => true,\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_HTTPHEADER => [\n \"Content-Type: application/json\",\n \"X-API-KEY: \",\n ],\n CURLOPT_POSTFIELDS => $payload,\n]);\n$audio = curl_exec($ch);\n$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);\nif ($status !== 200) {\n fwrite(STDERR, \"HTTP $status\\n\");\n exit(1);\n}\nfile_put_contents(\"output.wav\", $audio);\necho \"Saved \" . strlen($audio) . \" bytes to output.wav\\n\";\n" }, { "lang": "Go", "label": "Go (net/http)", "source": "package main\n\nimport (\n \"bytes\"\n \"fmt\"\n \"io\"\n \"net/http\"\n \"os\"\n)\n\nfunc main() {\n body := []byte(`{\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\": \"Everything is so incredibly perfect that I feel like I'm dreaming.\",\n \"model\": \"ssfm-v30\",\n \"language\": \"eng\",\n \"prompt\": {\n \"emotion_type\": \"smart\",\n \"previous_text\": \"I feel like I'm walking on air and I just want to scream with joy!\",\n \"next_text\": \"I am literally bursting with happiness and I never want this feeling to end!\"\n },\n \"output\": {\"volume\": 100, \"audio_pitch\": 0, \"audio_tempo\": 1, \"audio_format\": \"wav\"},\n \"seed\": 42\n }`)\n\n req, _ := http.NewRequest(\"POST\", \"https://api.typecast.ai/v1/text-to-speech\", bytes.NewReader(body))\n req.Header.Set(\"Content-Type\", \"application/json\")\n req.Header.Set(\"X-API-KEY\", \"\")\n\n resp, err := http.DefaultClient.Do(req)\n if err != nil {\n panic(err)\n }\n defer resp.Body.Close()\n\n out, _ := os.Create(\"output.wav\")\n defer out.Close()\n n, _ := io.Copy(out, resp.Body)\n fmt.Printf(\"Saved %d bytes to output.wav\\n\", n)\n}\n" }, { "lang": "Java", "label": "Java (HttpClient)", "source": "// Java 11+ HttpClient + file body handler.\nimport java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\nimport java.nio.file.Path;\n\npublic class TextToSpeech {\n public static void main(String[] args) throws Exception {\n String body = \"\"\"\n {\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\": \"Everything is so incredibly perfect that I feel like I'm dreaming.\",\n \"model\": \"ssfm-v30\",\n \"language\": \"eng\",\n \"prompt\": {\n \"emotion_type\": \"smart\",\n \"previous_text\": \"I feel like I'm walking on air and I just want to scream with joy!\",\n \"next_text\": \"I am literally bursting with happiness and I never want this feeling to end!\"\n },\n \"output\": {\"volume\": 100, \"audio_pitch\": 0, \"audio_tempo\": 1, \"audio_format\": \"wav\"},\n \"seed\": 42\n }\n \"\"\";\n\n HttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.typecast.ai/v1/text-to-speech\"))\n .header(\"Content-Type\", \"application/json\")\n .header(\"X-API-KEY\", \"\")\n .POST(HttpRequest.BodyPublishers.ofString(body))\n .build();\n\n HttpResponse response = HttpClient.newHttpClient()\n .send(request, HttpResponse.BodyHandlers.ofFile(Path.of(\"output.wav\")));\n\n System.out.println(\"Audio saved to \" + response.body());\n }\n}\n" }, { "lang": "Ruby", "label": "Ruby (net/http)", "source": "require \"net/http\"\nrequire \"uri\"\nrequire \"json\"\n\nuri = URI(\"https://api.typecast.ai/v1/text-to-speech\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nreq = Net::HTTP::Post.new(uri)\nreq[\"Content-Type\"] = \"application/json\"\nreq[\"X-API-KEY\"] = \"\"\nreq.body = {\n voice_id: \"tc_60e5426de8b95f1d3000d7b5\",\n text: \"Everything is so incredibly perfect that I feel like I'm dreaming.\",\n model: \"ssfm-v30\",\n language: \"eng\",\n prompt: {\n emotion_type: \"smart\",\n previous_text: \"I feel like I'm walking on air and I just want to scream with joy!\",\n next_text: \"I am literally bursting with happiness and I never want this feeling to end!\",\n },\n output: { volume: 100, audio_pitch: 0, audio_tempo: 1, audio_format: \"wav\" },\n seed: 42,\n}.to_json\n\nresp = http.request(req)\nraise \"HTTP #{resp.code}\" unless resp.code == \"200\"\n\nFile.binwrite(\"output.wav\", resp.body)\nputs \"Saved #{resp.body.bytesize} bytes to output.wav\"\n" }, { "lang": "cURL", "label": "무음 제거", "source": "curl --request POST 'https://api.typecast.ai/v1/text-to-speech' \\\n --header 'X-API-KEY: ' \\\n --header 'Content-Type: application/json' \\\n --output review.wav \\\n --data-binary @- <<'JSON'\n{\n \"voice_id\": \"\",\n \"text\": \"안녕하세요. 들어 주셔서 감사합니다.\",\n \"model\": \"ssfm-v30\",\n \"output\": {\n \"audio_format\": \"wav\",\n \"remove_silence_ms\": 300\n }\n}\nJSON\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "허용되는 최대 동시 요청 수" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "전체 커스텀 보이스 슬롯 한도 (professional 포함)" }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "사용 제한 정보" }, "Prompt": { "title": "프롬프트 (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "적용할 감정 프리셋.\r\n\r\nssfm-v21 지원 감정: normal, happy, sad, angry\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "example": 1, "description": "감정 표현 강도 제어(0.0~2.0).\r\n\r\n- 0.0: 완전히 중립적\r\n- 1.0: 표준 표현(기본값)\r\n- 2.0: 최대 강도\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "연령대 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **child**: 어린이 보이스(12세 미만)\n- **teenager**: 청소년 보이스(13-19세)\n- **young_adult**: 청년 보이스(20-35세)\n- **middle_age**: 중년 보이스(36-60세)\n- **elder**: 노년 보이스(60세 이상)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "플랜에서 기본으로 제공하는 총 크레딧" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "사용된 크레딧 수" } }, "description": "크레딧 사용 정보" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대 분류(어린이/청소년/청년/중년/노년)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별 분류(남성/여성)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "사용 가능한 감정이 있는 지원되는 TTS 모델 목록(예: [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유한 보이스 식별자. 기본 제공 보이스는 `tc_` prefix (예: `tc_60e5426de8b95f1d3000d7b5`), `POST /v1/voices/clone` 으로 만든 커스텀 보이스는 `uc_` prefix 를 사용합니다. 본인이 소유한 `uc_` 보이스도 `/v2/voices` 응답에 포함됩니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "이 보이스가 적합한 사용 사례 카테고리 목록\n\n## 보이스 이름 참고\n\n`voice_name` 필드는 영문으로만 반환됩니다. 아래 목록에서 한국어 표기를 확인하실 수 있습니다.\n\n
\n보이스 이름 목록 펼치기 (542개)\n\n| 영어 | 한국어 | 영어 | 한국어 | 영어 | 한국어 |\n| --- | --- | --- | --- | --- | --- |\n| Aaron | 에런 | Inhye | 인혜 | Piljae | 필재 |\n| Abigail | 아비게일 | Inseong | 인성 | Pyeonghwa | 평화 |\n| Ae-ran | 애란 | Instructor Han | 한유격 교관 | Rachel | 레이첼 |\n| Ael | 아엘 | Insun | 인선 | Ravi | 라비 |\n| Agatha | 아가사 | Jabbaba | 자바바 | Rayeon | 라연 |\n| Aiden | 에이든 | Jack | 잭 | Rebecca | 레베카 |\n| Alena | 알레나 | Jack-o’-Lantern | 잭오랜턴 | Reporter Bona | 다보나 기자 |\n| Alex | 알렉스 | Jackson | 잭슨 | Reporter Catalina | 카탈리나 기자 |\n| Amber | 엠버 | Jaeho | 재호 | Reporter Jinsub | 송진섭 기자 |\n| Anchor Hwa | 김경화 앵커 | Jaehun | 재훈 | Reporter Kang | 강수정 기자 |\n| Angela | 안젤라 | Jaejun | 재준 | Reporter Lee | 이승주 기자 |\n| Anja | 안냐 | Jaekyung | 재경 | Reporter Parkchan | 박찬 기자 |\n| Annabelle the Ghost | 귀신 애나벨 | Jaesun | 재선 | Rex | 렉스 |\n| Annie | 애니 | JaeYi | 재이 | Riley | 라일리 |\n| April | 에이프릴 | Jaeyoung | 재영 | Risan Ji | 지리산 |\n| Arang | 아랑 | Jain | 자인 | Rita | 리타 |\n| Ari | 아리 | Jake | 제이크 | River | 리버 |\n| Arin | 아린 | Janet | 재닛 | Robert | 로버트 |\n| Athena | 아테나 | Jangho | 장호 | Robo | 로보 |\n| Audrey | 오드리 | Jangwoon | 장운 | Romi | 로미 |\n| Avery | 에이버리 | Jechan | 제찬 | Ron | 론 |\n| Avong | 아봉 | Jeff | 제프 | Roro | 로로 |\n| Azzi | 아찌 | Jenna | 제나 | Rowoon | 로운 |\n| Babilon | 바빌론 | Jennifer | 제니퍼 | Royce | 로이스 |\n| Bell | 벨 | Jeong Choi | 최정 | Rudolph | 루돌프 |\n| Ben | 벤 | Jeongah | 정아 | Ruri | 루리 |\n| Benny | 베니 | JeongJin | 정진 | Rusty | 러스티 |\n| Beri | 베리 | Jeongseob | 정섭 | Ryan | 라이언 |\n| Billie | 빌리 | Jerome | 제롬 | Ryueun | 류은 |\n| Bomi | 이보미 | Jewby | 쥬비 | Sabrina the Witch | 마녀 사브리나 |\n| Bonggyu | 봉규 | Jian | 지안 | Saeron | 새론 |\n| Bongman Kim | 김봉만 | Jicheol | 지철 | Salty Chan-gu | 삐뚤어진 찬구 |\n| Bono | 보노 | Jihee | 지희 | Samantha | 사만다 |\n| Bora | 보라 | Jihoon | 지훈 | Sammy | 새미 |\n| Brad | 브래드 | Jihyun | 지현 | Sangdo | 상도 |\n| Bruce | 브루스 | Jimmy | 지미 | Sanghoon | 상훈 |\n| Buddy | 버디 | Jin | 진 | Sangwoo | 상우 |\n| Bumsoo | 범수 | Jinhan | 진한 | Santa | 산타 |\n| Buttaguy | 빠다가이 | Jinhang Kim | 김진행 | Santa Claus | 산타클로스 |\n| Caitlyn | 케이틀린 | Jinhee | 진희 | Santa Reporter | VJ 싼타 |\n| Callan | 칼란 | Jinhyuk | 진혁 | Sara | 세라 |\n| Camila | 카밀라 | Jinseo | 진서 | Sean | 션 |\n| Captain Bill | 캡틴 빌 | Jinung | 진웅 | SeHee | 세희 |\n| Carl | 칼 | Jinwoo | 진우 | Seheon | 세헌 |\n| Carlos | 카를로스 | Jiwoo | 지우 | Sejin | 세진 |\n| Carol | 캐롤 | Jiyoon | 지윤 | Sena | 세나 |\n| Catherine | 캐서린 | Jiyoung | 지영 | Seohee | 서희 |\n| Chad | 채드 | Jodie | 조디 | Seojin | 서진 |\n| Chaeah | 채아 | Jolly | 졸리 | Seojoon | 서준 |\n| Chan-gu | 찬구 | Jongdae | 종대 | Seok Choi | 최석 |\n| Changbae | 창배 | Jooeun | 주은 | SeokPil | 한석필 |\n| Changhee | 창희 | Joonghyun | 중현 | Seokpyo | 석표 |\n| Changmin | 창민 | Joongsik | 중식 | Seolhwa | 설화 |\n| Changsu | 박창수 | Joonkyu | 준규 | Seonha | 선하 |\n| Chanhyuk | 찬혁 | Joshua | 조슈아 | Seoyeon | 서연 |\n| Charlotte | 샬롯 | Juha | 주하 | Seoyoon | 서윤 |\n| Chase | 체이스 | Juho | 주호 | Seungah | 승아 |\n| Cheolhoon | 철훈 | Jungbong | 정봉 | Seungheon | 승헌 |\n| Cheolyong | 리철용 | Junghee | 정희 | Seungho | 승호 |\n| Cherry | 체리 | Jungjae | 정재 | Seunghwa | 승화 |\n| Chester | 체스터 | Jungmin | 정민 | Seungjae | 승재 |\n| Chiho | 치호 | Jungseok | 정석 | Seungmoon | 승문 |\n| Chloe | 클로이 | Jungsoon | 정순 | Seungwon | 승원 |\n| Choyeon | 강초연 교관 | Jungwon | 정원 | Seungyeon | 승연 |\n| Chungah | 청아 | Junhee | 준희 | Sewoo | 세우 |\n| Chunsik Kang | 강춘식 기자 | Junho | 준호 | Shana | 샤나 |\n| Claire | 클레어 | JunKi | 준기 | Sheriff Kim | 김반장 |\n| Classic Narrator | 미스타 변사 | Junsang | 준상 | Shinhe | 신혜 |\n| Cole | 콜 | Junseong | 준성 | Shinwook | 신욱 |\n| Cox | 콕스 | Justice Roh | 정의로 | Shotgun | 샷건 |\n| Cyrus | 사이러스 | Justin | 저스틴 | Simon | 사이먼 |\n| Dabin | 다빈 | Juwon | 주원 | Sindarin | 신다린 |\n| Daegil | 대길 | Juyoung | 주영 | Sio | 시오 |\n| Dahee | 다희 | K-Santa | 산타 할아버지 | Siwon | 시원 |\n| Dahyeon | 다현 | Kangil | 강일 | Siwoo | 시우 |\n| Damian | 데이미언 | Kanno | 칸노 | Siyeon | 시연 |\n| Dan | 댄 | Katie | 케이티 | Skylar | 스카일러 |\n| Dana | 다나 | Kelly | 켈리 | Slushy | 슬러시 |\n| Dasom | 다솜 | Kelsey | 켈시 | Smoke | 스모크 |\n| David | 데이비드 | Kevin | 케빈 | Sohye | 소혜 |\n| Dean | 딘 | Keybo | 키보 | Sojin | 소진 |\n| Deokhwan | 덕환 | Kijang Kim | 김기장 | Soobin | 수빈 |\n| Dohan | 도한 | Killian the Vampire | 뱀파이어 킬리언 | Sookhee | 숙희 |\n| Dohee | 도희 | Kiseob | 기섭 | Sooni | 순이 |\n| Dohyun | 도현 | Klip Kim | 클립 킴 | Sophia | 소피아 |\n| Dollar Jr. | 달러 주니어 | Koombo | 쿰보 | Sora | 소라 |\n| Doug | 더그 | Kristen | 크리스틴 | Soye | 소예 |\n| Doughnut | 도넛 | Kukhee | 국희 | Soyi | 소이 |\n| DU5T | 더스트 | Kwonil | 권일 | Soyoung | 소영 |\n| Duckchun | 덕춘 | Kyumin | 규민 | Soyul | 소율 |\n| Duke | 듀크 | Kyungho | 경호 | Spice | 스파이스 |\n| Dukgu | 덕구 | Kyungsoo | 경수 | Sportscaster Kang | 강수정 캐스터 |\n| Duman | 두만 | Kyungsook | 경숙 | Sportscaster Tony | 이영광 캐스터 |\n| Dupil | 곽두필 | Lady Cho | 단희빈 | Starling | 스탈링 |\n| DVZY | 데이지 | Lala | 라라 | Stephanie | 스테파니 |\n| Dylan | 딜런 | Lamie | 라미 | Storyteller Jinsub | 송진섭 스토리텔러 |\n| E-seller Sherri | 쇼린이 | Landon | 랜던 | Sua | 수아 |\n| Echo | 에코 | Larry | 래리 | Suho | 수호 |\n| Edward | 에드워드 | Leo | 레오 | Suji | 수지 |\n| Elias | 엘리아스 | Liam | 리암 | Sujin | 수진 |\n| Elise | 엘리스 | Lindsay | 린지 | Sullock Hong | 홍설록 |\n| Ella | 엘라 | Liz | 리즈 | Sumin | 수민 |\n| Eman | 이만 | Lloyd | 로이드 | Sungbae | 성배 |\n| Emma | 엠마 | Logan | 로건 | Sunggyu | 성규 |\n| Eunbin | 은빈 | Lucille | 루실 | Sungho | 성호 |\n| Eunchae | 은채 | Luna | 루나 | Sunghoon | 성훈 |\n| Eunha | 은하 | Lydia | 리디아 | Sunghyun | 성현 |\n| Eunsol | 은솔 | Maddie | 매디 | Sungjun | 홈쇼핑 성준 |\n| Frankenstein | 프랑켄슈타인 | Maisie | 메이시 | Sungkwon | 성권 |\n| Furnando | 터르난도 | Margaret | 마가렛 | Sungtae | 성태 |\n| Gaeul | 가을 | Margot | 마고 | Sungwook | 성욱 |\n| Gahee | 가희 | Matthew | 매튜 | Sunyoung | 선영 |\n| George | 조지 | MBTI EF (F) | MBTI EF 여 | Suyoon | 수윤 |\n| GeumHee | 금희 | MBTI EF (M) | MBTI EF 남 | Sylvia | 실비아 |\n| Geunhyeok | 근혁 | MBTI ET (F) | MBTI ET 여 | Taebaek | 장태백 |\n| Geunseok | 근석 | MBTI ET (M) | MBTI ET 남 | Taeji | 태지 |\n| Geunwoo | 근우 | MBTI IF (F) | MBTI IF 여 | Taejoong | 태중 |\n| Geunyeong | 근영 | MBTI IF (M) | MBTI IF 남 | Taemin | 태민 |\n| Ggami | 까미 | MBTI IT (F) | MBTI IT 여 | Taesub | 태섭 |\n| Ggomi | 꼬미 | MBTI IT (M) | MBTI IT 남 | Taewoo | 태우 |\n| Glenda | 글렌다 | MC Kong | MC콩 | Teal | 틸 |\n| Goat Kim | 김고트 | MC TypeCast | MC 타캐 | Tessa | 테사 |\n| Gongchul | 공철 | Mia | 미아 | Tian | 티안 |\n| Gowoon | 고운 | Michael | 마이클 | Tim | 팀 |\n| Grace | 그레이스 | Mija | 오미자 | Tina | 티나 |\n| Graham | 그레이엄 | Mijin | 미진 | Toby | 토비 |\n| Gunseok | 건석 | Mikyung | 미경 | Tommy | 토미 |\n| Gunwoo | 건우 | Millie | 밀리 | Tyson | 타이슨 |\n| Guri | 구리 | Minchae | 민채 | UiChan | 의찬이 |\n| Gus | 거스 | Minji | 민지 | Uncle Hank | 행크 |\n| Ha Eun | 하은 | Minjoon | 민준 | Valerie | 밸러리 |\n| Haejun | 해준 | Minju | 민주 | Valkyrie | 발키리 |\n| Haerang | 해랑 | Minjung | 민정 | Vanessa | 바네사 |\n| Hailey | 헤일리 | Minsang | 민상 | Verna | 베르나 |\n| Hajun | 하준 | Minsu | 민수 | Victoria | 빅토리아 |\n| Hakchul | 학철 | Mio | 미오 | Viktor | 빅토르 |\n| Hamchu | 햄쮸 | Miran Choi | 최미란 | Vincent | 빈센트 |\n| Han Taesung Caster | 한태성 캐스터 | Mirine | 미리내 | Vivien | 비비안 |\n| Hana | 하나 | Miseon | 미선 | Wade | 웨이드 |\n| Hangyeol | 한결 | Miso | 미소 | Walter | 월터 |\n| Hanjun | 한준 | Mister Gop | 미스터 갑 | Wangkwon | 왕권 |\n| Hanna | 한나 | Monggun | 몽군 | Weather Reporter Sky | 하늘 캐스터 |\n| Hans | 한스 | Moonjung | 문정 | West | 웨스트 |\n| Hansol | 한솔 | Mooyeol | 무열 | Wonho | 원호 |\n| Hanyoung | 한영 | Morgan | 모건 | Wonkyung | 원경 |\n| Harin | 하린 | Moru | 머루 | Wonwoo | 원우 |\n| Harper | 하퍼 | Mrs. Claus | 미세스 클로스 | Wooju | 우주 |\n| Hayul | 하율 | Munseok | 문석 | Woosung | 우성 |\n| Heejoon | 희준 | Munsu | 문수 | Xavier | 자비에르 |\n| Helena | 헬레나 | Myeonghee | 명희 | Yeeun | 예은 |\n| Henry | 헨리 | Myungil | 명일 | Yejin | 예진 |\n| Homun Sim | 심호문 | Myungjoo | 명주 | Yejoon | 예준 |\n| Hoon | 훈 | Naeun | 나은 | YeLin | 예린 |\n| Hosik | 호식 | Najin | 나진 | Yena | 예나 |\n| Hosun | 호선 | Namjoon | 남준 | Yeonah | 연아 |\n| Hoyoung | 호영 | Nana | 나나 | Yeonggeol | 영걸 |\n| Hugh | 휴 | Nari | 개나리 | Yeonhwa | 연화 |\n| Hwimin | 휘민 | Nathan | 네이선 | Yeonja | 연자 |\n| Hyejung | 혜정 | Neel | 닐 | Yeonsuh | 연서 |\n| Hyelee | 혜리 | Neoguard | 네오가드 | Yeonwoo | 연우 |\n| Hyemin | 혜민 | News anchor Jinsub | 송진섭 앵커 | Yeseul | 예슬 |\n| Hyena | 혜나 | Newscaster John | 존 앵커 | Yongsik | 용식이 |\n| Hyeongjin | 형진 | Nia | 니아 | Yoonseo | 윤서 |\n| Hyera | 혜라 | Noa | 노아 | Yoonsung | 윤성 |\n| Hyesu | 홈쇼핑 혜수 | Noel | 노엘 | Younggil | 영길 |\n| Hyewon | 혜원 | Noeul | 노을 | Younghee | 영희 |\n| Hyun | 현 | Norah | 노라 | Younghwan | 영환 |\n| Hyunji | 현지 | Nova | 노바 | Youngji | 영지 |\n| Hyunjin | 현진 | Old radio | 레디오 | Youngkyu | 영규 |\n| Hyunju | 현주 | Oliver | 올리버 | Youngmok | 영목 |\n| Hyunkyung | 현경 | Olivia | 올리비아 | Yubin | 유빈 |\n| Hyunmin | 현민 | Oscar | 오스카 | Yujin | 유진 |\n| Hyunseung | 현승 | Owen | 오언 | Yumi | 유미 |\n| HyunWoo | 현우 | P-0150N | 포이즌 | Yumin | 유민 |\n| Ian | 이안 | Pacang | 파캉 | Yunbin | 윤빈 |\n| Icarus | 이카루스 | Paige | 페이지 | Yunjeong | 윤정 |\n| Igyeom | 이겸 | Pangpang | 팡팡 | Yura | 유라 |\n| Ijun | 이준 | Patricia | 패트리샤 | Yuri | 유리 |\n| Ilho | 일호 | Patrick | 패트릭 | Yuseong | 유성 |\n| Ina | 이나 | Peter | 피터 | Zoey | 조이 |\n| Inhwa | 인화 | Philip | 필립 | | |\n\n
\n" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "사람이 읽을 수 있는 보이스 이름" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입 — `original` 은 타입캐스트가 기본 제공하는 스톡 보이스, `custom` 은 사용자가 업로드/클론한 보이스입니다." } }, "description": "모델별로 그룹화된 감정과 향상된 메타데이터가 있는 V2 보이스 응답 모델" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대입니다." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별(`male` 또는 `female`)입니다." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "지원하는 TTS 모델과 감정 목록입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유 보이스 식별자입니다. 기본 보이스는 `tc_`, 커스텀 보이스는 `uc_`로 시작합니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "추천 사용 사례 목록입니다." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "`{\"eng\": \"Daejin\", \"kor\": \"대진\"}` 같은 다국어 보이스 이름입니다." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입(`original` 또는 `custom`)입니다." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "제공되는 경우 사용할 수 있는 미리듣기 오디오 URL입니다." } }, "description": "다국어 이름과 미리듣기 URL을 포함한 보이스 정보입니다." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "API 플랜.\n\n사용 가능한 값:\n- **free**: 제한된 크레딧의 무료 등급\n- **lite**: 중간 수준 크레딧의 라이트 플랜\n- **plus**: 높은 크레딧의 플러스 플랜\n- **custom**: 커스텀 엔터프라이즈 플랜" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "음성 합성에 사용할 TTS 모델 버전. 다양한 모델은 다양한 기능과 품질 수준을 제공합니다.\n\n사용 가능한 모델:\n- **ssfm-v30**: 향상된 플로우와 추가 감정 프리셋이 있는 최신 모델(권장)\n- **ssfm-v21**: 검증된 신뢰성과 일관된 품질을 갖춘 안정적인 모델\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS 모델 버전(예: ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "이 모델에서 지원되는 감정 목록" } }, "description": "버전 및 지원되는 감정을 포함한 모델 정보" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "영어 보이스 이름입니다." }, "kor": { "type": "string", "title": "Kor", "description": "한국어 보이스 이름입니다." } }, "description": "ISO 639-3 언어 코드를 키로 사용하는 다국어 보이스 이름입니다." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "보이스 타입 분류.\n\n- `original` — 타입캐스트가 기본 제공하는 스톡 보이스로, 모든 계정에서 사용 가능합니다.\n- `custom` — 사용자가 본인의 샘플을 업로드하거나 클로닝해서 만든 보이스입니다." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "성별 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **male**: 남성 보이스\n- **female**: 여성 보이스\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "음성 합성에 사용 가능한 감정 프리셋. 각 감정은 생성된 음성의 톤, 속도, 표현력에 영향을 줍니다.\n\n**ssfm-v21 지원 감정 (4종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n\n**ssfm-v30 지원 감정 (7종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n- whisper: 부드럽고 조용한 말\n- toneup: 더 높은 톤 강조\n- tonedown: 더 낮은 톤 강조\n\n/v2/voices API 응답을 통해 각 음성에 사용 가능한 감정을 확인하세요.\n" }, "SmartPrompt": { "type": "object", "title": "스마트 프롬프트 (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "TTSRequest의 `text` 필드 이후에 오는 텍스트. 감정 추론을 위한 전방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 전환을 예측하는 데 도움\r\n- 다음 컨텍스트가 없으면 비워 둡니다\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "프롬프트 유형을 식별하는 판별자 필드. 컨텍스트 인식 감정 추론을 위해 \"smart\"로 설정해야 합니다.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "TTSRequest의 `text` 필드 이전에 오는 텍스트. 감정 추론을 위한 후방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 빌드업과 컨텍스트를 이해하는 데 도움\r\n- 이전 컨텍스트가 없으면 비워 둡니다\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "프리셋 프롬프트 (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "프롬프트 유형을 식별하는 판별자 필드. 프리셋 기반 감정 제어를 위해 \"preset\"으로 설정해야 합니다.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "생성된 음성에 적용할 감정 프리셋.\r\n\r\n지원되는 감정: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "생성된 음성의 감정 표현 강도를 제어합니다.\r\n\r\n- 0.0: 완전히 중립적, 감정 색채 없음\r\n- 0.5: 미묘한 감정 힌트\r\n- 1.0: 표준 감정 표현(기본값)\r\n- 1.5: 강한 감정 강조\r\n- 2.0: 최대 강도, 매우 표현력 있음\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "콘텐츠 유형 필터링을 위한 보이스 사용 사례 카테고리. 각 보이스는 특정 콘텐츠 유형에 대한 적합성을 나타내는 하나 이상의 사용 사례로 태그됩니다.\n\n**사용 가능한 사용 사례:**\n- **Announcer**: 공공 발표 및 프레젠테이션\n- **Anime**: 애니메이션 보이스\n- **Audiobook**: 장문 내레이션 및 스토리텔링\n- **Conversational**: 챗봇 및 대화형 AI\n- **Documentary**: 다큐멘터리 내레이션 및 해설\n- **E-learning**: 교육 콘텐츠 및 튜토리얼\n- **Rapper**: 랩 및 음악 퍼포먼스\n- **Game**: 비디오 게임 보이스 및 내레이션\n- **Tiktok/Reels**: SNS 숏츠\n- **News**: 뉴스 방송\n- **Podcast**: 방송 및 팟캐스트 제작\n- **Voicemail**: IVR 시스템 및 음성 비서\n- **Ads**: 광고 및 홍보 콘텐츠\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "문제를 설명하는 오류 메시지" }, "message": { "type": "string", "description": "구조화된 오류에 대한 설명" }, "error_code": { "type": "string", "description": "구조화된 오류를 식별하는 코드" } }, "description": "기존 오류와 유효성 검사 오류는 `detail`을 사용하며, 구조화된 오류는 `error_code`와 `message`를 사용합니다." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "`status`가 `failed`일 때의 실패 사유입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "source": { "type": "string", "title": "Source", "description": "생성 방식(`instant` 또는 `professional`)입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "UTC 기준 생성 시각입니다." } }, "description": "인증된 계정이 소유한 커스텀 보이스입니다." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "추천 관련도 점수. 값이 높을수록 검색어와 더 잘 맞는 후보입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "`tc_` prefix 가 붙은 타입캐스트 보이스 식별자. 텍스트 음성 변환 요청의 `voice_id` 로 사용할 수 있습니다." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "사람이 읽을 수 있는 보이스 이름." } }, "description": "`GET /v1/voices/recommendations` 응답의 보이스 추천 후보. 관련도 순서로 정렬됩니다." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "커스텀 보이스의 현재 생성 또는 학습 상태입니다." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름 (1~30자)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "보이스가 클로닝된 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "`uc_` prefix 가 붙은 커스텀 보이스 식별자. `POST /v1/text-to-speech` 등 `voice_id` 를 받는 엔드포인트에 그대로 사용할 수 있습니다." } }, "description": "`POST /v1/voices/clone` 응답 — 퀵 클로닝으로 생성된 커스텀 보이스 메타데이터." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "세그먼트 구분값입니다. 항상 `pause`입니다." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "초 단위 쉼 길이입니다. 개별 쉼은 최대 10초, 모든 쉼의 합은 최대 60초입니다.", "exclusiveMinimum": 0 } }, "description": "크레딧 차감 없이 삽입되는 무음 구간입니다.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 단어 단위 정렬 구간." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "현재 구독 플랜명" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "사용 제한 정보" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "크레딧 사용 정보" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "구독 정보 응답 모델" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호/공백 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 문자 단위 정렬 구간." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." } }, "description": "퀵 클로닝 또는 프리미엄 클로닝 생성 요청 결과입니다." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "base64 로 인코딩된 오디오 바이트. `audio_format` 확장자로 디코딩해 파일로 저장할 수 있습니다." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "단어 단위 타임스탬프(문장부호 포함). 요청이 `granularity=char` 일 때는 `null`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "문자 단위 타임스탬프(문장부호와 공백 포함). 요청이 `granularity=word` 일 때는 `null`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "`audio` 필드의 오디오 인코딩 포맷 — `wav` 또는 `mp3` (요청의 `output.audio_format` 에 따라 결정)." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "생성된 오디오의 길이(초)." } }, "description": "TTS 생성 + 타임스탬프 정렬 통합 응답." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "오디오 샘플. WAV 또는 MP3, 최대 25MB, 5초 이상 150초 이하.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "보이스 이름 (1~30자)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "클로닝할 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." } }, "description": "퀵 클로닝 요청을 위한 multipart 본문." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "파일", "format": "binary", "description": "25 MiB 이하이면서 길이가 5초 이상 150초 이하인 WAV 또는 MP3 음성 파일 1개입니다.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "상품 내레이터", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." } }, "description": "퀵 클로닝용 multipart 요청 본문입니다." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "커스텀 보이스 이름", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "파일", "description": "학습에 사용할 WAV 또는 MP3 음성 파일입니다. 하나의 파일을 업로드할 수 있습니다.\r\n\r\n**업로드 제한**\r\n\r\n* WAV 또는 MP3 파일 1개\r\n* 파일 크기: 1 GiB 이하\r\n* 길이: 5분 이상 3시간 이하\r\n* 샘플레이트: 16 kHz 이상\r\n\r\n최상의 결과를 위해 다음 조건을 충족하는 오디오를 권장합니다.\r\n\r\n* 생성된 보이스가 재현하기를 원하는 말투와 최대한 가깝게 녹음해 주세요.\r\n* 배경 소음이 없는 조용한 환경에서 녹음해 주세요.\r\n* 한 명의 화자 목소리만 포함해 주세요.\r\n* `language` 필드에 지정한 언어로 녹음해 주세요.\r\n* 오디오 길이가 길수록 생성되는 보이스의 품질이 향상됩니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." }, "language": { "type": "string", "title": "Language", "example": "kor", "description": "`kor`, `eng` 같은 ISO 639-3 언어 코드입니다." } }, "description": "프리미엄 클로닝용 multipart 요청 본문입니다." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "인증을 위한 API 키. 타입캐스트 API 콘솔에서 API 키를 생성할 수 있습니다." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 타임스탬프 포함 텍스트 음성 변환(TTS with Timestamps) > 텍스트로부터 음성을 생성하면서 **단어·문자 단위 타임스탬프**를 함께 반환합니다. 자막 싱크, 문자 단위 하이라이트 애니메이션, 발화 구간 시각화 등에 활용할 수 있습니다. 요청 본문은 표준 `/v1/text-to-speech` 엔드포인트와 동일합니다(voice_id, text, model, language, prompt, output, seed). 응답은 바이너리 오디오가 아닌 JSON 이며, base64 로 인코딩된 오디오와 함께 `words` / `characters` 배열을 포함합니다. 필요에 따라 `granularity` 쿼리 파라미터로 단어 단위 또는 문자 단위 중 한쪽만 받아 응답 크기를 줄일 수 있습니다. > **언어 주의.** 일본어(`jpn`), 중국어(`zho`) 처럼 단어 사이에 공백이 없는 언어는 word 단위 정렬이 문장 전체를 하나의 "단어" 로 묶어 버립니다. 이런 언어에서는 항상 `granularity=char` 를 지정해 문자 단위 타임스탬프를 받으세요. 사용 가능한 보이스 목록은 [보이스 목록 조회](/docs/ko/api-reference/voices/list-voices) 를 참조하세요. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "프로덕션 서버" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/text-to-speech/with-timestamps": { "post": { "tags": [ "Text-to-Speech" ], "x-mint": { "href": "/ko/api-reference/text-to-speech/text-to-speech-with-timestamps" }, "summary": "타임스탬프 포함 텍스트 음성 변환(TTS with Timestamps)", "security": [ { "ApiKeyAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TTSWithTimestampsResponse" }, "example": { "audio": "UklGRs...(base64 오디오 생략)", "words": [ { "end": 0.76, "text": "집중력이", "start": 0.08 }, { "end": 1.26, "text": "떨어질", "start": 0.8 }, { "end": 1.52, "text": "땐", "start": 1.3 }, { "end": 2.02, "text": "5분간", "start": 1.56 }, { "end": 2.7, "text": "스트레칭을", "start": 2.06 }, { "end": 3.2, "text": "해보세요.", "start": 2.74 } ], "characters": [ { "end": 0.26, "text": "집", "start": 0.08 }, { "end": 0.43, "text": "중", "start": 0.26 }, { "end": 0.6, "text": "력", "start": 0.43 }, { "end": 0.76, "text": "이", "start": 0.6 }, { "end": 0.8, "text": " ", "start": 0.76 }, { "end": 0.94, "text": "떨", "start": 0.8 }, { "end": 1.1, "text": "어", "start": 0.94 }, { "end": 1.26, "text": "질", "start": 1.1 }, { "end": 1.3, "text": " ", "start": 1.26 }, { "end": 1.52, "text": "땐", "start": 1.3 }, { "end": 1.56, "text": " ", "start": 1.52 }, { "end": 1.68, "text": "5", "start": 1.56 }, { "end": 1.84, "text": "분", "start": 1.68 }, { "end": 2.02, "text": "간", "start": 1.84 }, { "end": 2.06, "text": " ", "start": 2.02 }, { "end": 2.18, "text": "스", "start": 2.06 }, { "end": 2.3, "text": "트", "start": 2.18 }, { "end": 2.42, "text": "레", "start": 2.3 }, { "end": 2.56, "text": "칭", "start": 2.42 }, { "end": 2.7, "text": "을", "start": 2.56 }, { "end": 2.74, "text": " ", "start": 2.7 }, { "end": 2.88, "text": "해", "start": 2.74 }, { "end": 3.02, "text": "보", "start": 2.88 }, { "end": 3.14, "text": "세", "start": 3.02 }, { "end": 3.18, "text": "요", "start": 3.14 }, { "end": 3.2, "text": ".", "start": 3.18 } ], "audio_format": "wav", "audio_duration": 3.2 } } }, "description": "Success - Returns base64 audio and timestamps" }, "400": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid voice_id" } } }, "description": "Bad Request - Invalid parameters" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Unauthorized - Authentication failed" }, "402": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Insufficient credit" } } }, "description": "Payment Required - Insufficient credits" }, "404": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Voice not found" } } }, "description": "Not Found - Voice model not available" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" } } }, "description": "유효성 검사 오류 - 요청이 올바르지 않거나 입력 텍스트를 합성할 수 없는 경우" }, "429": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Too many requests" } } }, "description": "Too Many Requests - Rate limit exceeded" }, "500": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "An unexpected error occurred" } } }, "description": "Internal Server Error - TTS generation or timestamp alignment failed" } }, "deprecated": false, "parameters": [ { "in": "query", "name": "granularity", "schema": { "enum": [ "word", "char" ], "type": "string" }, "required": false, "description": "반환할 타임스탬프 배열을 선택합니다.\r\n\r\n* 생략: `words` 와 `characters` 모두 반환\r\n* `word`: `words` 만 반환 (`characters` 는 null)\r\n* `char`: `characters` 만 반환 (`words` 는 null)\r\n\r\n**공백 없는 언어(예: `jpn`, `zho`):** `word` 정렬은 문장 전체를 하나의 구간으로 반환하므로, 의미 있는 타임스탬프를 얻으려면 `char` 를 사용하세요." } ], "description": "텍스트로부터 음성을 생성하면서 **단어·문자 단위 타임스탬프**를 함께 반환합니다. 자막 싱크, 문자 단위 하이라이트 애니메이션, 발화 구간 시각화 등에 활용할 수 있습니다.\n\n요청 본문은 표준 `/v1/text-to-speech` 엔드포인트와 동일합니다(voice_id, text, model, language, prompt, output, seed). 응답은 바이너리 오디오가 아닌 JSON 이며, base64 로 인코딩된 오디오와 함께 `words` / `characters` 배열을 포함합니다.\n\n필요에 따라 `granularity` 쿼리 파라미터로 단어 단위 또는 문자 단위 중 한쪽만 받아 응답 크기를 줄일 수 있습니다.\n\n> **언어 주의.** 일본어(`jpn`), 중국어(`zho`) 처럼 단어 사이에 공백이 없는 언어는 word 단위 정렬이 문장 전체를 하나의 \"단어\" 로 묶어 버립니다. 이런 언어에서는 항상 `granularity=char` 를 지정해 문자 단위 타임스탬프를 받으세요.\n\n사용 가능한 보이스 목록은 [보이스 목록 조회](/docs/ko/api-reference/voices/list-voices) 를 참조하세요.", "operationId": "text_to_speech_with_timestamps_v1_text_to_speech_with_timestamps_post", "requestBody": { "content": { "application/json": { "schema": { "type": "object", "title": "TTSRequestWith-timestampsWith-timestamps", "required": [ "voice_id", "text", "model" ], "properties": { "seed": { "type": "integer", "anyOf": [ { "type": "integer", "maximum": 4294967295, "minimum": 0 }, { "type": "null" } ], "title": "Seed", "format": "uint32", "example": 42, "minimum": 0, "description": "재현 가능한 음성 생성을 위한 부호 없는 정수 시드. 동일한 시드와 동일한 입력 파라미터로 항상 같은 오디오 결과를 생성합니다.\r\n\r\n* 0 이상의 정수만 허용됩니다. 음수 값은 사용할 수 없습니다.\r\n* 생략하면 서버가 매번 랜덤 시드를 생성하여 약간의 변이가 발생합니다." }, "text": { "type": "string", "title": "Text", "example": "모든 것이 너무나 완벽해서 마치 꿈을 꾸는 것 같습니다.", "maxLength": 2000, "minLength": 1, "description": "음성으로 변환할 텍스트. 최소 1자, 최대 2000자. 텍스트 길이에 따라 크레딧이 소비됩니다. 영어, 한국어, 일본어, 중국어를 포함한 여러 언어를 지원합니다. 특수 문자와 구두점은 자동으로 처리됩니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "음성 합성에 사용할 보이스 모델.\r\n\r\n* **ssfm-v30**: 향상된 플로우와 추가 감정 프리셋이 있는 최신 모델(권장)\r\n* **ssfm-v21**: 빠르고 안정적인 모델로 신뢰할 수 있는 품질 제공" }, "output": { "type": "object", "title": "", "properties": { "volume": { "anyOf": [ { "type": "integer", "maximum": 200, "minimum": 0 }, { "type": "null" } ], "title": "Volume", "example": 100, "description": "출력 음성의 상대적인 음량 조절: 0(완전 무음), 50(절반 볼륨), 100(표준 볼륨, 기본값), 150(표준보다 50% 크게), 200(최대 볼륨, 표준의 두 배).\r\n\r\n출력된 음성마다 음량이 다를 경우, 단순 비율 조절인 `volume`을 사용하면 음성 간의 음량 편차가 더욱 커질 수 있습니다. 일정한 음량 출력이 필요한 경우 `target_lufs` 사용을 권장합니다.\r\n\r\n- **주의:** `target_lufs`와 동시에 사용할 수 없습니다.\r\n\r\n필수 범위: 0 <= x <= 200\r\n" }, "audio_pitch": { "type": "integer", "title": "Audio Pitch", "default": 0, "example": 0, "maximum": 12, "minimum": -12, "description": "성별과 나이에 영향을 주는 반음 단위의 피치 조정: -12(한 옥타브 낮게, 더 깊은 목소리), -6(반 옥타브 낮게), 0(원래 피치, 기본값), +6(반 옥타브 높게), +12(한 옥타브 높게, 더 높은 목소리)" }, "audio_tempo": { "type": "number", "title": "Audio Tempo", "default": 1, "example": 1, "maximum": 2, "minimum": 0.5, "description": "음성 속도 제어: 0.5(절반 속도, 매우 느리고 명확함), 0.75(보통보다 약간 느림), 1.0(보통 말하기 속도, 기본값), 1.5(보통보다 50% 빠름), 2.0(두 배 속도, 매우 빠른 음성)" }, "target_lufs": { "type": "integer", "anyOf": [ { "type": "number", "maximum": 0, "minimum": -70 }, { "type": "null" } ], "title": "Target Lufs", "example": -14, "description": "출력 음성의 목표 절대 음량(LUFS) 설정. 원본 음성의 크기와 상관없이 모든 음성을 일정한 크기로 정규화하여 생성합니다. 값이 0에 가까울수록 소리가 커지며, -70에 가까울수록 작아집니다.\r\n\r\n- 필수 범위: -70 <= x <= 0\r\n- 권장값: -14 (일반적인 스트리밍 표준), -23 (방송 표준)\r\n- **주의:** `volume` 파라미터와 함께 사용할 수 없습니다. 절대적인 음량 기준이 필요할 때는 `target_lufs`를, 상대적인 비율 조절이 필요할 때는 `volume`을 선택하여 사용하세요.\r\n" }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "default": "wav", "example": "wav", "description": "출력 오디오 형식.\r\n\r\n**WAV 형식:**\r\n- 비압축 PCM 오디오\r\n- 16비트 깊이, 모노 채널, 44100 Hz 샘플링 속도\r\n- 더 높은 품질, 더 큰 파일 크기\r\n- 전문 오디오 제작에 권장\r\n\r\n**MP3 형식:**\r\n- 압축된 MPEG Layer III 오디오\r\n- 320 kbps 비트레이트, 44100 Hz 샘플링 속도\r\n- 더 작은 파일 크기\r\n- 웹 스트리밍 및 배포에 권장\r\n" }, "remove_silence_ms": { "anyOf": [ { "type": "integer", "maximum": 1000, "minimum": 0 }, { "type": "null" } ], "title": "무음 구간 길이 (ms)", "default": null, "example": 100, "description": "무음 제거를 적용하면 음성 내 무음 구간 중 지정한 길이보다 긴 구간을 해당 길이로 줄입니다. 단위는 밀리초(ms)입니다. 지정값은 제거할 시간이 아니라 남길 무음 길이입니다.\r\n\r\n**입력값:**\r\n- **0부터 1000까지**의 정수, 권장 범위는 0~200.\r\n- 생략 또는 `null`: 무음 제거를 적용하지 않습니다.\r\n- `0`: 0ms 초과의 무음을 제거합니다. **기능을 끄는 값이 아닙니다.**\r\n- 불리언·문자열·소수·범위 밖 값은 허용하지 않습니다.\r\n\r\n**예시:** `100`을 지정하면 음성 내 무음 구간 중 100ms보다 긴 구간을 100ms로 줄입니다. \r\n값이 작을수록 더 짧은 무음 구간까지 제거 대상에 포함되고, 각 구간에서 남기는 무음도 짧아집니다." } }, "description": "볼륨(0-200), 피치(-12\\~+12 반음), 템포(0.5배\\~2.0배), 형식(wav/mp3)을 포함한 오디오 출력 설정으로 최종 오디오 특성을 제어합니다\r\n\r\n`remove_silence_ms`(정수, 0\\~1000ms)로 검출된 무음 구간을 줄일 수 있습니다." }, "prompt": { "oneOf": [ { "$ref": "#/components/schemas/SmartPrompt" }, { "$ref": "#/components/schemas/PresetPrompt" }, { "$ref": "#/components/schemas/Prompt" } ], "title": "Prompt", "description": "생성된 음성의 감정 및 스타일 설정, 감정 유형(happy/sad/angry/normal) 및 강도(0.0\\~2.0)를 포함하여 감정 표현을 제어합니다" }, "language": { "type": "string", "title": "Language", "example": "kor", "description": "ISO 639-3 표준을 따르는 언어 코드. 대소문자 구분 안 함(\"KOR\"과 \"kor\" 모두 허용). 제공하지 않으면 텍스트 내용을 기반으로 자동 감지됩니다.\r\n\r\n
\r\n ssfm-v30 지원 언어 (37개)\r\n\r\n | 코드 | 언어 | 코드 | 언어 | 코드 | 언어 |\r\n | --- | ------ | --- | ------ | --- | ------ |\r\n | ARA | 아랍어 | IND | 인도네시아어 | POR | 포르투갈어 |\r\n | BEN | 벵골어 | ITA | 이탈리아어 | RON | 루마니아어 |\r\n | BUL | 불가리아어 | JPN | 일본어 | RUS | 러시아어 |\r\n | CES | 체코어 | KOR | 한국어 | SLK | 슬로바키아어 |\r\n | DAN | 덴마크어 | MSA | 말레이어 | SPA | 스페인어 |\r\n | DEU | 독일어 | NAN | 민남어 | SWE | 스웨덴어 |\r\n | ELL | 그리스어 | NLD | 네덜란드어 | TAM | 타밀어 |\r\n | ENG | 영어 | NOR | 노르웨이어 | TGL | 타갈로그어 |\r\n | FIN | 핀란드어 | PAN | 펀자브어 | THA | 태국어 |\r\n | FRA | 프랑스어 | POL | 폴란드어 | TUR | 터키어 |\r\n | HIN | 힌디어 | UKR | 우크라이나어 | VIE | 베트남어 |\r\n | HRV | 크로아티아어 | YUE | 광둥어 | ZHO | 중국어 |\r\n | HUN | 헝가리어 | | | | |\r\n
\r\n\r\n
\r\n ssfm-v21 지원 언어 (27개)\r\n\r\n | 코드 | 언어 | 코드 | 언어 | 코드 | 언어 |\r\n | --- | ----- | --- | ------ | --- | ------ |\r\n | ARA | 아랍어 | IND | 인도네시아어 | RON | 루마니아어 |\r\n | BUL | 불가리아어 | ITA | 이탈리아어 | RUS | 러시아어 |\r\n | CES | 체코어 | JPN | 일본어 | SLK | 슬로바키아어 |\r\n | DAN | 덴마크어 | KOR | 한국어 | SPA | 스페인어 |\r\n | DEU | 독일어 | MSA | 말레이어 | SWE | 스웨덴어 |\r\n | ELL | 그리스어 | NLD | 네덜란드어 | TAM | 타밀어 |\r\n | ENG | 영어 | POL | 폴란드어 | TGL | 타갈로그어 |\r\n | FIN | 핀란드어 | POR | 포르투갈어 | UKR | 우크라이나어 |\r\n | FRA | 프랑스어 | HRV | 크로아티아어 | ZHO | 중국어 |\r\n
\r\n\r\n> **타임스탬프 엔드포인트 주의.** 일본어(`jpn`) · 중국어(`zho`) 처럼 단어 사이에 공백이 없는 언어는 word 단위 정렬이 문장 전체를 하나의 구간으로 묶어 버립니다. 이런 언어에서는 항상 `granularity=char` 를 함께 지정해 문자 단위 타임스탬프를 받으세요." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "보이스 식별자. 두 가지 prefix 를 지원합니다.\r\n\r\n* `tc_` — 기본 제공되는 타입캐스트 보이스 (예: `tc_60e5426de8b95f1d3000d7b5`). 사용 가능한 ID 는 [보이스 목록 조회](/docs/ko/api-reference/voices/list-voices) 를 참조하세요.\r\n* `uc_` — [퀵 클로닝](/docs/ko/api-reference/voices/instant-cloning) 으로 생성한 커스텀 보이스 (예: `uc_64a1b2c3d4e5f6a7b8c9d0e1`). 본인이 소유한 클로닝 보이스만 사용할 수 있습니다.\r\n\r\n대소문자 구분: prefix 는 소문자만 사용합니다." } }, "description": "TTSRequestWith-timestamps parameters" } } }, "required": true, "description": "" }, "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request POST \\\n --url https://api.typecast.ai/v1/text-to-speech/with-timestamps \\\n --header 'Content-Type: application/json' \\\n --header 'X-API-KEY: ' \\\n --data @- <\",\n \"Content-Type\": \"application/json\",\n}\npayload = {\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\": \"집중력이 떨어질 땐 5분간 스트레칭을 해보세요.\",\n \"model\": \"ssfm-v30\",\n \"language\": \"kor\",\n \"prompt\": {\n \"emotion_type\": \"preset\",\n \"emotion_preset\": \"normal\",\n \"emotion_intensity\": 1.0,\n },\n}\n\nresponse = requests.post(\n f\"{API_HOST}/v1/text-to-speech/with-timestamps\",\n headers=headers,\n json=payload,\n timeout=60,\n)\nresponse.raise_for_status()\ndata = response.json()\n\nwith open(\"output.wav\", \"wb\") as f:\n f.write(base64.b64decode(data[\"audio\"]))\nprint(f\"저장 완료: duration={data['audio_duration']}초\")\nfor w in (data.get(\"words\") or [])[:3]:\n print(f\" 단어: {w['text']!r} {w['start']:.3f}s - {w['end']:.3f}s\")\n" }, { "lang": "cURL", "label": "무음 제거", "source": "curl --request POST 'https://api.typecast.ai/v1/text-to-speech/with-timestamps' \\\n --header 'X-API-KEY: ' \\\n --header 'Content-Type: application/json' \\\n --output review.json \\\n --data-binary @- <<'JSON'\n{\n \"voice_id\": \"\",\n \"text\": \"안녕하세요. 들어 주셔서 감사합니다.\",\n \"model\": \"ssfm-v30\",\n \"output\": {\n \"audio_format\": \"wav\",\n \"remove_silence_ms\": 300\n }\n}\nJSON\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "허용되는 최대 동시 요청 수" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "전체 커스텀 보이스 슬롯 한도 (professional 포함)" }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "사용 제한 정보" }, "Prompt": { "title": "프롬프트 (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "적용할 감정 프리셋.\r\n\r\nssfm-v21 지원 감정: normal, happy, sad, angry\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "example": 1, "description": "감정 표현 강도 제어(0.0~2.0).\r\n\r\n- 0.0: 완전히 중립적\r\n- 1.0: 표준 표현(기본값)\r\n- 2.0: 최대 강도\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "연령대 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **child**: 어린이 보이스(12세 미만)\n- **teenager**: 청소년 보이스(13-19세)\n- **young_adult**: 청년 보이스(20-35세)\n- **middle_age**: 중년 보이스(36-60세)\n- **elder**: 노년 보이스(60세 이상)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "플랜에서 기본으로 제공하는 총 크레딧" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "사용된 크레딧 수" } }, "description": "크레딧 사용 정보" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대 분류(어린이/청소년/청년/중년/노년)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별 분류(남성/여성)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "사용 가능한 감정이 있는 지원되는 TTS 모델 목록(예: [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유한 보이스 식별자. 기본 제공 보이스는 `tc_` prefix (예: `tc_60e5426de8b95f1d3000d7b5`), `POST /v1/voices/clone` 으로 만든 커스텀 보이스는 `uc_` prefix 를 사용합니다. 본인이 소유한 `uc_` 보이스도 `/v2/voices` 응답에 포함됩니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "이 보이스가 적합한 사용 사례 카테고리 목록\n\n## 보이스 이름 참고\n\n`voice_name` 필드는 영문으로만 반환됩니다. 아래 목록에서 한국어 표기를 확인하실 수 있습니다.\n\n
\n보이스 이름 목록 펼치기 (542개)\n\n| 영어 | 한국어 | 영어 | 한국어 | 영어 | 한국어 |\n| --- | --- | --- | --- | --- | --- |\n| Aaron | 에런 | Inhye | 인혜 | Piljae | 필재 |\n| Abigail | 아비게일 | Inseong | 인성 | Pyeonghwa | 평화 |\n| Ae-ran | 애란 | Instructor Han | 한유격 교관 | Rachel | 레이첼 |\n| Ael | 아엘 | Insun | 인선 | Ravi | 라비 |\n| Agatha | 아가사 | Jabbaba | 자바바 | Rayeon | 라연 |\n| Aiden | 에이든 | Jack | 잭 | Rebecca | 레베카 |\n| Alena | 알레나 | Jack-o’-Lantern | 잭오랜턴 | Reporter Bona | 다보나 기자 |\n| Alex | 알렉스 | Jackson | 잭슨 | Reporter Catalina | 카탈리나 기자 |\n| Amber | 엠버 | Jaeho | 재호 | Reporter Jinsub | 송진섭 기자 |\n| Anchor Hwa | 김경화 앵커 | Jaehun | 재훈 | Reporter Kang | 강수정 기자 |\n| Angela | 안젤라 | Jaejun | 재준 | Reporter Lee | 이승주 기자 |\n| Anja | 안냐 | Jaekyung | 재경 | Reporter Parkchan | 박찬 기자 |\n| Annabelle the Ghost | 귀신 애나벨 | Jaesun | 재선 | Rex | 렉스 |\n| Annie | 애니 | JaeYi | 재이 | Riley | 라일리 |\n| April | 에이프릴 | Jaeyoung | 재영 | Risan Ji | 지리산 |\n| Arang | 아랑 | Jain | 자인 | Rita | 리타 |\n| Ari | 아리 | Jake | 제이크 | River | 리버 |\n| Arin | 아린 | Janet | 재닛 | Robert | 로버트 |\n| Athena | 아테나 | Jangho | 장호 | Robo | 로보 |\n| Audrey | 오드리 | Jangwoon | 장운 | Romi | 로미 |\n| Avery | 에이버리 | Jechan | 제찬 | Ron | 론 |\n| Avong | 아봉 | Jeff | 제프 | Roro | 로로 |\n| Azzi | 아찌 | Jenna | 제나 | Rowoon | 로운 |\n| Babilon | 바빌론 | Jennifer | 제니퍼 | Royce | 로이스 |\n| Bell | 벨 | Jeong Choi | 최정 | Rudolph | 루돌프 |\n| Ben | 벤 | Jeongah | 정아 | Ruri | 루리 |\n| Benny | 베니 | JeongJin | 정진 | Rusty | 러스티 |\n| Beri | 베리 | Jeongseob | 정섭 | Ryan | 라이언 |\n| Billie | 빌리 | Jerome | 제롬 | Ryueun | 류은 |\n| Bomi | 이보미 | Jewby | 쥬비 | Sabrina the Witch | 마녀 사브리나 |\n| Bonggyu | 봉규 | Jian | 지안 | Saeron | 새론 |\n| Bongman Kim | 김봉만 | Jicheol | 지철 | Salty Chan-gu | 삐뚤어진 찬구 |\n| Bono | 보노 | Jihee | 지희 | Samantha | 사만다 |\n| Bora | 보라 | Jihoon | 지훈 | Sammy | 새미 |\n| Brad | 브래드 | Jihyun | 지현 | Sangdo | 상도 |\n| Bruce | 브루스 | Jimmy | 지미 | Sanghoon | 상훈 |\n| Buddy | 버디 | Jin | 진 | Sangwoo | 상우 |\n| Bumsoo | 범수 | Jinhan | 진한 | Santa | 산타 |\n| Buttaguy | 빠다가이 | Jinhang Kim | 김진행 | Santa Claus | 산타클로스 |\n| Caitlyn | 케이틀린 | Jinhee | 진희 | Santa Reporter | VJ 싼타 |\n| Callan | 칼란 | Jinhyuk | 진혁 | Sara | 세라 |\n| Camila | 카밀라 | Jinseo | 진서 | Sean | 션 |\n| Captain Bill | 캡틴 빌 | Jinung | 진웅 | SeHee | 세희 |\n| Carl | 칼 | Jinwoo | 진우 | Seheon | 세헌 |\n| Carlos | 카를로스 | Jiwoo | 지우 | Sejin | 세진 |\n| Carol | 캐롤 | Jiyoon | 지윤 | Sena | 세나 |\n| Catherine | 캐서린 | Jiyoung | 지영 | Seohee | 서희 |\n| Chad | 채드 | Jodie | 조디 | Seojin | 서진 |\n| Chaeah | 채아 | Jolly | 졸리 | Seojoon | 서준 |\n| Chan-gu | 찬구 | Jongdae | 종대 | Seok Choi | 최석 |\n| Changbae | 창배 | Jooeun | 주은 | SeokPil | 한석필 |\n| Changhee | 창희 | Joonghyun | 중현 | Seokpyo | 석표 |\n| Changmin | 창민 | Joongsik | 중식 | Seolhwa | 설화 |\n| Changsu | 박창수 | Joonkyu | 준규 | Seonha | 선하 |\n| Chanhyuk | 찬혁 | Joshua | 조슈아 | Seoyeon | 서연 |\n| Charlotte | 샬롯 | Juha | 주하 | Seoyoon | 서윤 |\n| Chase | 체이스 | Juho | 주호 | Seungah | 승아 |\n| Cheolhoon | 철훈 | Jungbong | 정봉 | Seungheon | 승헌 |\n| Cheolyong | 리철용 | Junghee | 정희 | Seungho | 승호 |\n| Cherry | 체리 | Jungjae | 정재 | Seunghwa | 승화 |\n| Chester | 체스터 | Jungmin | 정민 | Seungjae | 승재 |\n| Chiho | 치호 | Jungseok | 정석 | Seungmoon | 승문 |\n| Chloe | 클로이 | Jungsoon | 정순 | Seungwon | 승원 |\n| Choyeon | 강초연 교관 | Jungwon | 정원 | Seungyeon | 승연 |\n| Chungah | 청아 | Junhee | 준희 | Sewoo | 세우 |\n| Chunsik Kang | 강춘식 기자 | Junho | 준호 | Shana | 샤나 |\n| Claire | 클레어 | JunKi | 준기 | Sheriff Kim | 김반장 |\n| Classic Narrator | 미스타 변사 | Junsang | 준상 | Shinhe | 신혜 |\n| Cole | 콜 | Junseong | 준성 | Shinwook | 신욱 |\n| Cox | 콕스 | Justice Roh | 정의로 | Shotgun | 샷건 |\n| Cyrus | 사이러스 | Justin | 저스틴 | Simon | 사이먼 |\n| Dabin | 다빈 | Juwon | 주원 | Sindarin | 신다린 |\n| Daegil | 대길 | Juyoung | 주영 | Sio | 시오 |\n| Dahee | 다희 | K-Santa | 산타 할아버지 | Siwon | 시원 |\n| Dahyeon | 다현 | Kangil | 강일 | Siwoo | 시우 |\n| Damian | 데이미언 | Kanno | 칸노 | Siyeon | 시연 |\n| Dan | 댄 | Katie | 케이티 | Skylar | 스카일러 |\n| Dana | 다나 | Kelly | 켈리 | Slushy | 슬러시 |\n| Dasom | 다솜 | Kelsey | 켈시 | Smoke | 스모크 |\n| David | 데이비드 | Kevin | 케빈 | Sohye | 소혜 |\n| Dean | 딘 | Keybo | 키보 | Sojin | 소진 |\n| Deokhwan | 덕환 | Kijang Kim | 김기장 | Soobin | 수빈 |\n| Dohan | 도한 | Killian the Vampire | 뱀파이어 킬리언 | Sookhee | 숙희 |\n| Dohee | 도희 | Kiseob | 기섭 | Sooni | 순이 |\n| Dohyun | 도현 | Klip Kim | 클립 킴 | Sophia | 소피아 |\n| Dollar Jr. | 달러 주니어 | Koombo | 쿰보 | Sora | 소라 |\n| Doug | 더그 | Kristen | 크리스틴 | Soye | 소예 |\n| Doughnut | 도넛 | Kukhee | 국희 | Soyi | 소이 |\n| DU5T | 더스트 | Kwonil | 권일 | Soyoung | 소영 |\n| Duckchun | 덕춘 | Kyumin | 규민 | Soyul | 소율 |\n| Duke | 듀크 | Kyungho | 경호 | Spice | 스파이스 |\n| Dukgu | 덕구 | Kyungsoo | 경수 | Sportscaster Kang | 강수정 캐스터 |\n| Duman | 두만 | Kyungsook | 경숙 | Sportscaster Tony | 이영광 캐스터 |\n| Dupil | 곽두필 | Lady Cho | 단희빈 | Starling | 스탈링 |\n| DVZY | 데이지 | Lala | 라라 | Stephanie | 스테파니 |\n| Dylan | 딜런 | Lamie | 라미 | Storyteller Jinsub | 송진섭 스토리텔러 |\n| E-seller Sherri | 쇼린이 | Landon | 랜던 | Sua | 수아 |\n| Echo | 에코 | Larry | 래리 | Suho | 수호 |\n| Edward | 에드워드 | Leo | 레오 | Suji | 수지 |\n| Elias | 엘리아스 | Liam | 리암 | Sujin | 수진 |\n| Elise | 엘리스 | Lindsay | 린지 | Sullock Hong | 홍설록 |\n| Ella | 엘라 | Liz | 리즈 | Sumin | 수민 |\n| Eman | 이만 | Lloyd | 로이드 | Sungbae | 성배 |\n| Emma | 엠마 | Logan | 로건 | Sunggyu | 성규 |\n| Eunbin | 은빈 | Lucille | 루실 | Sungho | 성호 |\n| Eunchae | 은채 | Luna | 루나 | Sunghoon | 성훈 |\n| Eunha | 은하 | Lydia | 리디아 | Sunghyun | 성현 |\n| Eunsol | 은솔 | Maddie | 매디 | Sungjun | 홈쇼핑 성준 |\n| Frankenstein | 프랑켄슈타인 | Maisie | 메이시 | Sungkwon | 성권 |\n| Furnando | 터르난도 | Margaret | 마가렛 | Sungtae | 성태 |\n| Gaeul | 가을 | Margot | 마고 | Sungwook | 성욱 |\n| Gahee | 가희 | Matthew | 매튜 | Sunyoung | 선영 |\n| George | 조지 | MBTI EF (F) | MBTI EF 여 | Suyoon | 수윤 |\n| GeumHee | 금희 | MBTI EF (M) | MBTI EF 남 | Sylvia | 실비아 |\n| Geunhyeok | 근혁 | MBTI ET (F) | MBTI ET 여 | Taebaek | 장태백 |\n| Geunseok | 근석 | MBTI ET (M) | MBTI ET 남 | Taeji | 태지 |\n| Geunwoo | 근우 | MBTI IF (F) | MBTI IF 여 | Taejoong | 태중 |\n| Geunyeong | 근영 | MBTI IF (M) | MBTI IF 남 | Taemin | 태민 |\n| Ggami | 까미 | MBTI IT (F) | MBTI IT 여 | Taesub | 태섭 |\n| Ggomi | 꼬미 | MBTI IT (M) | MBTI IT 남 | Taewoo | 태우 |\n| Glenda | 글렌다 | MC Kong | MC콩 | Teal | 틸 |\n| Goat Kim | 김고트 | MC TypeCast | MC 타캐 | Tessa | 테사 |\n| Gongchul | 공철 | Mia | 미아 | Tian | 티안 |\n| Gowoon | 고운 | Michael | 마이클 | Tim | 팀 |\n| Grace | 그레이스 | Mija | 오미자 | Tina | 티나 |\n| Graham | 그레이엄 | Mijin | 미진 | Toby | 토비 |\n| Gunseok | 건석 | Mikyung | 미경 | Tommy | 토미 |\n| Gunwoo | 건우 | Millie | 밀리 | Tyson | 타이슨 |\n| Guri | 구리 | Minchae | 민채 | UiChan | 의찬이 |\n| Gus | 거스 | Minji | 민지 | Uncle Hank | 행크 |\n| Ha Eun | 하은 | Minjoon | 민준 | Valerie | 밸러리 |\n| Haejun | 해준 | Minju | 민주 | Valkyrie | 발키리 |\n| Haerang | 해랑 | Minjung | 민정 | Vanessa | 바네사 |\n| Hailey | 헤일리 | Minsang | 민상 | Verna | 베르나 |\n| Hajun | 하준 | Minsu | 민수 | Victoria | 빅토리아 |\n| Hakchul | 학철 | Mio | 미오 | Viktor | 빅토르 |\n| Hamchu | 햄쮸 | Miran Choi | 최미란 | Vincent | 빈센트 |\n| Han Taesung Caster | 한태성 캐스터 | Mirine | 미리내 | Vivien | 비비안 |\n| Hana | 하나 | Miseon | 미선 | Wade | 웨이드 |\n| Hangyeol | 한결 | Miso | 미소 | Walter | 월터 |\n| Hanjun | 한준 | Mister Gop | 미스터 갑 | Wangkwon | 왕권 |\n| Hanna | 한나 | Monggun | 몽군 | Weather Reporter Sky | 하늘 캐스터 |\n| Hans | 한스 | Moonjung | 문정 | West | 웨스트 |\n| Hansol | 한솔 | Mooyeol | 무열 | Wonho | 원호 |\n| Hanyoung | 한영 | Morgan | 모건 | Wonkyung | 원경 |\n| Harin | 하린 | Moru | 머루 | Wonwoo | 원우 |\n| Harper | 하퍼 | Mrs. Claus | 미세스 클로스 | Wooju | 우주 |\n| Hayul | 하율 | Munseok | 문석 | Woosung | 우성 |\n| Heejoon | 희준 | Munsu | 문수 | Xavier | 자비에르 |\n| Helena | 헬레나 | Myeonghee | 명희 | Yeeun | 예은 |\n| Henry | 헨리 | Myungil | 명일 | Yejin | 예진 |\n| Homun Sim | 심호문 | Myungjoo | 명주 | Yejoon | 예준 |\n| Hoon | 훈 | Naeun | 나은 | YeLin | 예린 |\n| Hosik | 호식 | Najin | 나진 | Yena | 예나 |\n| Hosun | 호선 | Namjoon | 남준 | Yeonah | 연아 |\n| Hoyoung | 호영 | Nana | 나나 | Yeonggeol | 영걸 |\n| Hugh | 휴 | Nari | 개나리 | Yeonhwa | 연화 |\n| Hwimin | 휘민 | Nathan | 네이선 | Yeonja | 연자 |\n| Hyejung | 혜정 | Neel | 닐 | Yeonsuh | 연서 |\n| Hyelee | 혜리 | Neoguard | 네오가드 | Yeonwoo | 연우 |\n| Hyemin | 혜민 | News anchor Jinsub | 송진섭 앵커 | Yeseul | 예슬 |\n| Hyena | 혜나 | Newscaster John | 존 앵커 | Yongsik | 용식이 |\n| Hyeongjin | 형진 | Nia | 니아 | Yoonseo | 윤서 |\n| Hyera | 혜라 | Noa | 노아 | Yoonsung | 윤성 |\n| Hyesu | 홈쇼핑 혜수 | Noel | 노엘 | Younggil | 영길 |\n| Hyewon | 혜원 | Noeul | 노을 | Younghee | 영희 |\n| Hyun | 현 | Norah | 노라 | Younghwan | 영환 |\n| Hyunji | 현지 | Nova | 노바 | Youngji | 영지 |\n| Hyunjin | 현진 | Old radio | 레디오 | Youngkyu | 영규 |\n| Hyunju | 현주 | Oliver | 올리버 | Youngmok | 영목 |\n| Hyunkyung | 현경 | Olivia | 올리비아 | Yubin | 유빈 |\n| Hyunmin | 현민 | Oscar | 오스카 | Yujin | 유진 |\n| Hyunseung | 현승 | Owen | 오언 | Yumi | 유미 |\n| HyunWoo | 현우 | P-0150N | 포이즌 | Yumin | 유민 |\n| Ian | 이안 | Pacang | 파캉 | Yunbin | 윤빈 |\n| Icarus | 이카루스 | Paige | 페이지 | Yunjeong | 윤정 |\n| Igyeom | 이겸 | Pangpang | 팡팡 | Yura | 유라 |\n| Ijun | 이준 | Patricia | 패트리샤 | Yuri | 유리 |\n| Ilho | 일호 | Patrick | 패트릭 | Yuseong | 유성 |\n| Ina | 이나 | Peter | 피터 | Zoey | 조이 |\n| Inhwa | 인화 | Philip | 필립 | | |\n\n
\n" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "사람이 읽을 수 있는 보이스 이름" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입 — `original` 은 타입캐스트가 기본 제공하는 스톡 보이스, `custom` 은 사용자가 업로드/클론한 보이스입니다." } }, "description": "모델별로 그룹화된 감정과 향상된 메타데이터가 있는 V2 보이스 응답 모델" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대입니다." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별(`male` 또는 `female`)입니다." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "지원하는 TTS 모델과 감정 목록입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유 보이스 식별자입니다. 기본 보이스는 `tc_`, 커스텀 보이스는 `uc_`로 시작합니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "추천 사용 사례 목록입니다." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "`{\"eng\": \"Daejin\", \"kor\": \"대진\"}` 같은 다국어 보이스 이름입니다." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입(`original` 또는 `custom`)입니다." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "제공되는 경우 사용할 수 있는 미리듣기 오디오 URL입니다." } }, "description": "다국어 이름과 미리듣기 URL을 포함한 보이스 정보입니다." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "API 플랜.\n\n사용 가능한 값:\n- **free**: 제한된 크레딧의 무료 등급\n- **lite**: 중간 수준 크레딧의 라이트 플랜\n- **plus**: 높은 크레딧의 플러스 플랜\n- **custom**: 커스텀 엔터프라이즈 플랜" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "음성 합성에 사용할 TTS 모델 버전. 다양한 모델은 다양한 기능과 품질 수준을 제공합니다.\n\n사용 가능한 모델:\n- **ssfm-v30**: 향상된 플로우와 추가 감정 프리셋이 있는 최신 모델(권장)\n- **ssfm-v21**: 검증된 신뢰성과 일관된 품질을 갖춘 안정적인 모델\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS 모델 버전(예: ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "이 모델에서 지원되는 감정 목록" } }, "description": "버전 및 지원되는 감정을 포함한 모델 정보" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "영어 보이스 이름입니다." }, "kor": { "type": "string", "title": "Kor", "description": "한국어 보이스 이름입니다." } }, "description": "ISO 639-3 언어 코드를 키로 사용하는 다국어 보이스 이름입니다." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "보이스 타입 분류.\n\n- `original` — 타입캐스트가 기본 제공하는 스톡 보이스로, 모든 계정에서 사용 가능합니다.\n- `custom` — 사용자가 본인의 샘플을 업로드하거나 클로닝해서 만든 보이스입니다." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "성별 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **male**: 남성 보이스\n- **female**: 여성 보이스\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "음성 합성에 사용 가능한 감정 프리셋. 각 감정은 생성된 음성의 톤, 속도, 표현력에 영향을 줍니다.\n\n**ssfm-v21 지원 감정 (4종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n\n**ssfm-v30 지원 감정 (7종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n- whisper: 부드럽고 조용한 말\n- toneup: 더 높은 톤 강조\n- tonedown: 더 낮은 톤 강조\n\n/v2/voices API 응답을 통해 각 음성에 사용 가능한 감정을 확인하세요.\n" }, "SmartPrompt": { "type": "object", "title": "스마트 프롬프트 (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "TTSRequest의 `text` 필드 이후에 오는 텍스트. 감정 추론을 위한 전방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 전환을 예측하는 데 도움\r\n- 다음 컨텍스트가 없으면 비워 둡니다\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "프롬프트 유형을 식별하는 판별자 필드. 컨텍스트 인식 감정 추론을 위해 \"smart\"로 설정해야 합니다.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "TTSRequest의 `text` 필드 이전에 오는 텍스트. 감정 추론을 위한 후방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 빌드업과 컨텍스트를 이해하는 데 도움\r\n- 이전 컨텍스트가 없으면 비워 둡니다\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "프리셋 프롬프트 (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "프롬프트 유형을 식별하는 판별자 필드. 프리셋 기반 감정 제어를 위해 \"preset\"으로 설정해야 합니다.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "생성된 음성에 적용할 감정 프리셋.\r\n\r\n지원되는 감정: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "생성된 음성의 감정 표현 강도를 제어합니다.\r\n\r\n- 0.0: 완전히 중립적, 감정 색채 없음\r\n- 0.5: 미묘한 감정 힌트\r\n- 1.0: 표준 감정 표현(기본값)\r\n- 1.5: 강한 감정 강조\r\n- 2.0: 최대 강도, 매우 표현력 있음\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "콘텐츠 유형 필터링을 위한 보이스 사용 사례 카테고리. 각 보이스는 특정 콘텐츠 유형에 대한 적합성을 나타내는 하나 이상의 사용 사례로 태그됩니다.\n\n**사용 가능한 사용 사례:**\n- **Announcer**: 공공 발표 및 프레젠테이션\n- **Anime**: 애니메이션 보이스\n- **Audiobook**: 장문 내레이션 및 스토리텔링\n- **Conversational**: 챗봇 및 대화형 AI\n- **Documentary**: 다큐멘터리 내레이션 및 해설\n- **E-learning**: 교육 콘텐츠 및 튜토리얼\n- **Rapper**: 랩 및 음악 퍼포먼스\n- **Game**: 비디오 게임 보이스 및 내레이션\n- **Tiktok/Reels**: SNS 숏츠\n- **News**: 뉴스 방송\n- **Podcast**: 방송 및 팟캐스트 제작\n- **Voicemail**: IVR 시스템 및 음성 비서\n- **Ads**: 광고 및 홍보 콘텐츠\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "문제를 설명하는 오류 메시지" }, "message": { "type": "string", "description": "구조화된 오류에 대한 설명" }, "error_code": { "type": "string", "description": "구조화된 오류를 식별하는 코드" } }, "description": "기존 오류와 유효성 검사 오류는 `detail`을 사용하며, 구조화된 오류는 `error_code`와 `message`를 사용합니다." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "`status`가 `failed`일 때의 실패 사유입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "source": { "type": "string", "title": "Source", "description": "생성 방식(`instant` 또는 `professional`)입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "UTC 기준 생성 시각입니다." } }, "description": "인증된 계정이 소유한 커스텀 보이스입니다." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "추천 관련도 점수. 값이 높을수록 검색어와 더 잘 맞는 후보입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "`tc_` prefix 가 붙은 타입캐스트 보이스 식별자. 텍스트 음성 변환 요청의 `voice_id` 로 사용할 수 있습니다." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "사람이 읽을 수 있는 보이스 이름." } }, "description": "`GET /v1/voices/recommendations` 응답의 보이스 추천 후보. 관련도 순서로 정렬됩니다." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "커스텀 보이스의 현재 생성 또는 학습 상태입니다." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름 (1~30자)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "보이스가 클로닝된 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "`uc_` prefix 가 붙은 커스텀 보이스 식별자. `POST /v1/text-to-speech` 등 `voice_id` 를 받는 엔드포인트에 그대로 사용할 수 있습니다." } }, "description": "`POST /v1/voices/clone` 응답 — 퀵 클로닝으로 생성된 커스텀 보이스 메타데이터." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "세그먼트 구분값입니다. 항상 `pause`입니다." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "초 단위 쉼 길이입니다. 개별 쉼은 최대 10초, 모든 쉼의 합은 최대 60초입니다.", "exclusiveMinimum": 0 } }, "description": "크레딧 차감 없이 삽입되는 무음 구간입니다.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 단어 단위 정렬 구간." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "현재 구독 플랜명" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "사용 제한 정보" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "크레딧 사용 정보" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "구독 정보 응답 모델" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호/공백 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 문자 단위 정렬 구간." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." } }, "description": "퀵 클로닝 또는 프리미엄 클로닝 생성 요청 결과입니다." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "base64 로 인코딩된 오디오 바이트. `audio_format` 확장자로 디코딩해 파일로 저장할 수 있습니다." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "단어 단위 타임스탬프(문장부호 포함). 요청이 `granularity=char` 일 때는 `null`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "문자 단위 타임스탬프(문장부호와 공백 포함). 요청이 `granularity=word` 일 때는 `null`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "`audio` 필드의 오디오 인코딩 포맷 — `wav` 또는 `mp3` (요청의 `output.audio_format` 에 따라 결정)." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "생성된 오디오의 길이(초)." } }, "description": "TTS 생성 + 타임스탬프 정렬 통합 응답." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "오디오 샘플. WAV 또는 MP3, 최대 25MB, 5초 이상 150초 이하.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "보이스 이름 (1~30자)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "클로닝할 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." } }, "description": "퀵 클로닝 요청을 위한 multipart 본문." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "파일", "format": "binary", "description": "25 MiB 이하이면서 길이가 5초 이상 150초 이하인 WAV 또는 MP3 음성 파일 1개입니다.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "상품 내레이터", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." } }, "description": "퀵 클로닝용 multipart 요청 본문입니다." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "커스텀 보이스 이름", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "파일", "description": "학습에 사용할 WAV 또는 MP3 음성 파일입니다. 하나의 파일을 업로드할 수 있습니다.\r\n\r\n**업로드 제한**\r\n\r\n* WAV 또는 MP3 파일 1개\r\n* 파일 크기: 1 GiB 이하\r\n* 길이: 5분 이상 3시간 이하\r\n* 샘플레이트: 16 kHz 이상\r\n\r\n최상의 결과를 위해 다음 조건을 충족하는 오디오를 권장합니다.\r\n\r\n* 생성된 보이스가 재현하기를 원하는 말투와 최대한 가깝게 녹음해 주세요.\r\n* 배경 소음이 없는 조용한 환경에서 녹음해 주세요.\r\n* 한 명의 화자 목소리만 포함해 주세요.\r\n* `language` 필드에 지정한 언어로 녹음해 주세요.\r\n* 오디오 길이가 길수록 생성되는 보이스의 품질이 향상됩니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." }, "language": { "type": "string", "title": "Language", "example": "kor", "description": "`kor`, `eng` 같은 ISO 639-3 언어 코드입니다." } }, "description": "프리미엄 클로닝용 multipart 요청 본문입니다." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "인증을 위한 API 키. 타입캐스트 API 콘솔에서 API 키를 생성할 수 있습니다." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 스트리밍 텍스트 음성 변환(Streaming TTS) > 실시간 스트리밍을 사용하여 텍스트에서 음성을 생성합니다. 전체 합성이 완료되기 전에 오디오 재생을 시작할 수 있습니다. 이 엔드포인트는 오디오 데이터를 청크 단위로 스트리밍하여 즉각적인 피드백이 필요한 애플리케이션에서 낮은 지연 시간의 오디오 재생을 가능하게 합니다. **스트리밍 형식:** * **WAV 형식**: 첫 번째 청크에는 WAV 헤더(스트리밍용 size\=0xFFFFFFFF)와 원시 PCM 데이터가 포함됩니다. 이후 청크에는 PCM 데이터만 포함됩니다. * **MP3 형식**: 각 청크에는 독립적으로 디코딩할 수 있는 후처리된 MP3 데이터가 포함됩니다. **사용 사례:** * 대화형 AI, 챗봇, 실시간 음성 비서 등 * 즉각적인 오디오 피드백이 필요한 인터랙티브 애플리케이션 * 전체 합성을 기다리는 것이 비실용적인 장문 콘텐츠 **요청 파라미터:** 표준 TTS 엔드포인트와 동일한 TTSRequest 스키마를 사용합니다. `output.audio_format`을 "wav" 또는 "mp3"로 설정하여 스트리밍 형식을 제어합니다. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "프로덕션 서버" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/text-to-speech/stream": { "post": { "tags": [ "Text-to-Speech" ], "x-mint": { "href": "/ko/api-reference/text-to-speech/streaming-text-to-speech" }, "summary": "스트리밍 텍스트 음성 변환(Streaming TTS)", "security": [ { "ApiKeyAuth": [] } ], "responses": { "200": { "content": { "audio/wav": { "schema": { "type": "string", "format": "binary", "description": "청크 단위 WAV 오디오 스트림(16비트, 모노, 32000 Hz). 첫 번째 청크에는 size 0xFFFFFFFF(스트리밍 표시)의 WAV 헤더와 원시 PCM 데이터가 포함됩니다. 이후 청크에는 PCM 데이터만 포함됩니다." }, "example": "[Binary audio stream - WAV chunks]" } }, "description": "Success - Returns streaming audio data in chunks" }, "400": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid voice_id" } } }, "description": "Bad Request - Invalid parameters" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Unauthorized - Authentication failed" }, "402": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Insufficient credit" } } }, "description": "Payment Required - Insufficient credits" }, "404": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Voice not found" } } }, "description": "Not Found - Voice model not available" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" } } }, "description": "유효성 검사 오류 - 요청이 올바르지 않거나 입력 텍스트를 합성할 수 없는 경우입니다.\r\n스트리밍 시작 전에 감지된 입력 오류는 `TEXT_NOT_SYNTHESIZABLE`로 응답합니다. 스트리밍 응답이 시작된 뒤에는 HTTP 상태 코드를 변경할 수 없습니다." }, "429": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Too many requests" } } }, "description": "Too Many Requests - Rate limit exceeded" }, "500": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "An unexpected error occurred" } } }, "description": "Internal Server Error - Server processing failed" } }, "deprecated": false, "description": "실시간 스트리밍을 사용하여 텍스트에서 음성을 생성합니다. 전체 합성이 완료되기 전에 오디오 재생을 시작할 수 있습니다.\n\n이 엔드포인트는 오디오 데이터를 청크 단위로 스트리밍하여 즉각적인 피드백이 필요한 애플리케이션에서 낮은 지연 시간의 오디오 재생을 가능하게 합니다.\n\n**스트리밍 형식:**\n\n* **WAV 형식**: 첫 번째 청크에는 WAV 헤더(스트리밍용 size\\=0xFFFFFFFF)와 원시 PCM 데이터가 포함됩니다. 이후 청크에는 PCM 데이터만 포함됩니다.\n* **MP3 형식**: 각 청크에는 독립적으로 디코딩할 수 있는 후처리된 MP3 데이터가 포함됩니다.\n\n**사용 사례:**\n\n* 대화형 AI, 챗봇, 실시간 음성 비서 등\n* 즉각적인 오디오 피드백이 필요한 인터랙티브 애플리케이션\n* 전체 합성을 기다리는 것이 비실용적인 장문 콘텐츠\n\n**요청 파라미터:**\n표준 TTS 엔드포인트와 동일한 TTSRequest 스키마를 사용합니다. `output.audio_format`을 \"wav\" 또는 \"mp3\"로 설정하여 스트리밍 형식을 제어합니다.", "operationId": "text_to_speech_stream_v1_text_to_speech_stream_post", "requestBody": { "content": { "application/json": { "schema": { "type": "object", "title": "TTSRequestStreamStream", "required": [ "voice_id", "text", "model" ], "properties": { "seed": { "type": "integer", "anyOf": [ { "type": "integer", "maximum": 4294967295, "minimum": 0 }, { "type": "null" } ], "title": "Seed", "format": "uint32", "example": 42, "minimum": 0, "description": "재현 가능한 음성 생성을 위한 부호 없는 정수 시드. 동일한 시드와 동일한 입력 파라미터로 항상 같은 오디오 결과를 생성합니다.\r\n\r\n* 0 이상의 정수만 허용됩니다. 음수 값은 사용할 수 없습니다.\r\n* 생략하면 서버가 매번 랜덤 시드를 생성하여 약간의 변이가 발생합니다." }, "text": { "type": "string", "title": "Text", "example": "모든 것이 너무나 완벽해서 마치 꿈을 꾸는 것 같습니다.", "maxLength": 2000, "minLength": 1, "description": "음성으로 변환할 텍스트. 최소 1자, 최대 2000자. 텍스트 길이에 따라 크레딧이 소비됩니다. 영어, 한국어, 일본어, 중국어를 포함한 여러 언어를 지원합니다. 특수 문자와 구두점은 자동으로 처리됩니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "음성 합성에 사용할 보이스 모델.\r\n\r\n* **ssfm-v30**: 향상된 플로우와 추가 감정 프리셋이 있는 최신 모델(권장)\r\n* **ssfm-v21**: 빠르고 안정적인 모델로 신뢰할 수 있는 품질 제공" }, "output": { "type": "object", "title": "", "properties": { "audio_pitch": { "type": "integer", "title": "Audio Pitch", "default": 0, "example": 0, "maximum": 12, "minimum": -12, "description": "성별과 나이에 영향을 주는 반음 단위의 피치 조정: -12(한 옥타브 낮게, 더 깊은 목소리), -6(반 옥타브 낮게), 0(원래 피치, 기본값), +6(반 옥타브 높게), +12(한 옥타브 높게, 더 높은 목소리)" }, "audio_tempo": { "type": "number", "title": "Audio Tempo", "default": 1, "example": 1, "maximum": 2, "minimum": 0.5, "description": "음성 속도 제어: 0.5(절반 속도, 매우 느리고 명확함), 0.75(보통보다 약간 느림), 1.0(보통 말하기 속도, 기본값), 1.5(보통보다 50% 빠름), 2.0(두 배 속도, 매우 빠른 음성)" }, "target_lufs": { "type": "integer", "anyOf": [ { "type": "number", "maximum": 0, "minimum": -70 }, { "type": "null" } ], "title": "Target Lufs", "example": -14, "description": "스트리밍 출력 음성의 목표 절대 음량(LUFS) 설정. 원본 음성의 크기와 상관없이 일정한 라우드니스로 정규화합니다. `volume` 파라미터와 함께 사용할 수 없습니다.\r\n\r\n권장값: -14(일반적인 스트리밍 표준), -23(방송 표준).\r\n" }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "default": "wav", "example": "wav", "description": "스트리밍용 출력 오디오 형식.\r\n\r\n**WAV 형식:**\r\n- 비압축 PCM 오디오\r\n- 16비트 깊이, 모노 채널, **32000 Hz** 샘플링 속도\r\n- 청크 단위 전송: 첫 번째 청크는 WAV 헤더(size = 0xFFFFFFFF)를 포함하고, 이후 청크에는 원시 PCM 데이터가 이어집니다\r\n- 도착하는 즉시 오디오를 재생하고 싶을 때 권장\r\n\r\n**MP3 형식:**\r\n- 압축된 MPEG Layer III 오디오\r\n- 320 kbps 비트레이트, 44100 Hz 샘플링 속도\r\n- 청크 단위 전송: 각 청크에는 독립적으로 디코딩 가능한 MPEG 프레임이 포함됩니다\r\n- 대역폭이 제한된 클라이언트에 권장\r\n" }, "remove_silence_ms": { "anyOf": [ { "type": "integer", "maximum": 1000, "minimum": 0 }, { "type": "null" } ], "title": "무음 구간 길이 (ms)", "default": null, "example": 300, "description": "무음 제거를 적용하면 음성 내 무음 구간 중 지정한 길이보다 긴 구간을 해당 길이로 줄입니다. 단위는 밀리초(ms)입니다. 지정값은 제거할 시간이 아니라 남길 무음 길이입니다.\r\n\r\n**입력값:**\r\n- **0부터 1000까지**의 정수, 권장 범위는 0~200.\r\n- 생략 또는 `null`: 무음 제거를 적용하지 않습니다.\r\n- `0`: 0ms 초과의 무음을 제거합니다. **기능을 끄는 값이 아닙니다.**\r\n- 불리언·문자열·소수·범위 밖 값은 허용하지 않습니다.\r\n\r\n**예시:** `100`을 지정하면 음성 내 무음 구간 중 100ms보다 긴 구간을 100ms로 줄입니다. \r\n값이 작을수록 더 짧은 무음 구간까지 제거 대상에 포함되고, 각 구간에서 남기는 무음도 짧아집니다." } }, "description": "피치(-12 \\~ +12 반음), 속도(0.5x \\~ 2.0x), 형식(wav/mp3), target\\_lufs(-70 \\~ 0 LUFS) 등 스트리밍 오디오 출력 설정. 참고: 스트리밍 모드에서는 volume을 사용할 수 없습니다.\r\n\r\n무음 제거를 위한 `remove_silence_ms`(정수, 0\\~1000ms)도 지원합니다." }, "prompt": { "oneOf": [ { "$ref": "#/components/schemas/SmartPrompt" }, { "$ref": "#/components/schemas/PresetPrompt" }, { "$ref": "#/components/schemas/Prompt" } ], "title": "Prompt", "description": "생성된 음성의 감정 및 스타일 설정, 감정 유형(happy/sad/angry/normal) 및 강도(0.0\\~2.0)를 포함하여 감정 표현을 제어합니다" }, "language": { "type": "string", "title": "Language", "example": "kor", "description": "ISO 639-3 표준을 따르는 언어 코드. 대소문자 구분 안 함(\"KOR\"과 \"kor\" 모두 허용). 제공하지 않으면 텍스트 내용을 기반으로 자동 감지됩니다.\r\n\r\n
\r\n ssfm-v30 지원 언어 (37개)\r\n\r\n | 코드 | 언어 | 코드 | 언어 | 코드 | 언어 |\r\n | --- | ------ | --- | ------ | --- | ------ |\r\n | ARA | 아랍어 | IND | 인도네시아어 | POR | 포르투갈어 |\r\n | BEN | 벵골어 | ITA | 이탈리아어 | RON | 루마니아어 |\r\n | BUL | 불가리아어 | JPN | 일본어 | RUS | 러시아어 |\r\n | CES | 체코어 | KOR | 한국어 | SLK | 슬로바키아어 |\r\n | DAN | 덴마크어 | MSA | 말레이어 | SPA | 스페인어 |\r\n | DEU | 독일어 | NAN | 민남어 | SWE | 스웨덴어 |\r\n | ELL | 그리스어 | NLD | 네덜란드어 | TAM | 타밀어 |\r\n | ENG | 영어 | NOR | 노르웨이어 | TGL | 타갈로그어 |\r\n | FIN | 핀란드어 | PAN | 펀자브어 | THA | 태국어 |\r\n | FRA | 프랑스어 | POL | 폴란드어 | TUR | 터키어 |\r\n | HIN | 힌디어 | UKR | 우크라이나어 | VIE | 베트남어 |\r\n | HRV | 크로아티아어 | YUE | 광둥어 | ZHO | 중국어 |\r\n | HUN | 헝가리어 | | | | |\r\n
\r\n\r\n
\r\n ssfm-v21 지원 언어 (27개)\r\n\r\n | 코드 | 언어 | 코드 | 언어 | 코드 | 언어 |\r\n | --- | ----- | --- | ------ | --- | ------ |\r\n | ARA | 아랍어 | IND | 인도네시아어 | RON | 루마니아어 |\r\n | BUL | 불가리아어 | ITA | 이탈리아어 | RUS | 러시아어 |\r\n | CES | 체코어 | JPN | 일본어 | SLK | 슬로바키아어 |\r\n | DAN | 덴마크어 | KOR | 한국어 | SPA | 스페인어 |\r\n | DEU | 독일어 | MSA | 말레이어 | SWE | 스웨덴어 |\r\n | ELL | 그리스어 | NLD | 네덜란드어 | TAM | 타밀어 |\r\n | ENG | 영어 | POL | 폴란드어 | TGL | 타갈로그어 |\r\n | FIN | 핀란드어 | POR | 포르투갈어 | UKR | 우크라이나어 |\r\n | FRA | 프랑스어 | HRV | 크로아티아어 | ZHO | 중국어 |\r\n
" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "보이스 식별자. 두 가지 prefix 를 지원합니다.\r\n\r\n* `tc_` — 기본 제공되는 타입캐스트 보이스 (예: `tc_60e5426de8b95f1d3000d7b5`). 사용 가능한 ID 는 [보이스 목록 조회](/docs/ko/api-reference/voices/list-voices) 를 참조하세요.\r\n* `uc_` — [퀵 클로닝](/docs/ko/api-reference/voices/instant-cloning) 으로 생성한 커스텀 보이스 (예: `uc_64a1b2c3d4e5f6a7b8c9d0e1`). 본인이 소유한 클로닝 보이스만 사용할 수 있습니다.\r\n\r\n대소문자 구분: prefix 는 소문자만 사용합니다." } }, "description": "스트리밍 텍스트 음성 변환 요청 파라미터" } } }, "required": true, "description": "" }, "x-codeSamples": [ { "lang": "cURL", "label": "cURL (스트리밍 + 재생)", "source": "# 스트리밍 오디오를 ffplay로 파이핑하여 실시간 재생.\n# 사전 설치: ffmpeg (brew/choco/apt install ffmpeg)\ncurl -N -s --request POST \\\n --url https://api.typecast.ai/v1/text-to-speech/stream \\\n --header 'Content-Type: application/json' \\\n --header 'X-API-KEY: ' \\\n --data @- <\", \"Content-Type\": \"application/json\"}\npayload = {\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\": \"문의해 주셔서 감사합니다. 금요일 오후 7시로 예약이 확정되었습니다.\",\n \"model\": \"ssfm-v30\",\n}\n\nresp = requests.post(\n f\"{API_HOST}/v1/text-to-speech/stream\",\n headers=headers, json=payload, stream=True, timeout=60,\n)\nresp.raise_for_status()\n\nwith sd.RawOutputStream(samplerate=32000, channels=1, dtype=\"int16\") as player:\n buf, first = bytearray(), True\n for chunk in resp.iter_content(chunk_size=4096):\n if not chunk:\n continue\n if first:\n chunk = chunk[44:] # WAV 헤더 제거\n first = False\n buf.extend(chunk)\n # int16 샘플 정렬을 위해 2바이트 단위로만 write.\n n = len(buf) - (len(buf) % 2)\n if n:\n player.write(bytes(buf[:n]))\n del buf[:n]\n\nprint(\"재생 완료\")\n" }, { "lang": "C#", "label": "C# (HttpClient + ffplay)", "source": "// 스트림을 ffplay로 파이핑하여 실시간 재생.\n// 사전 설치: ffmpeg (brew/choco/apt install ffmpeg)\nusing System;\nusing System.Diagnostics;\nusing System.Net.Http;\nusing System.Text;\nusing System.Threading.Tasks;\n\nvar client = new HttpClient();\nclient.DefaultRequestHeaders.Add(\"X-API-KEY\", \"\");\n\nvar requestBody = @\"{\n \"\"voice_id\"\": \"\"tc_60e5426de8b95f1d3000d7b5\"\",\n \"\"text\"\": \"\"문의해 주셔서 감사합니다. 금요일 오후 7시로 예약이 확정되었습니다.\"\",\n \"\"model\"\": \"\"ssfm-v30\"\"\n}\";\n\nvar ffplay = new Process\n{\n StartInfo = new ProcessStartInfo\n {\n FileName = \"ffplay\",\n Arguments = \"-autoexit -nodisp -loglevel error -i pipe:0\",\n RedirectStandardInput = true,\n UseShellExecute = false,\n }\n};\nffplay.Start();\n\nvar request = new HttpRequestMessage(HttpMethod.Post, \"https://api.typecast.ai/v1/text-to-speech/stream\")\n{\n Content = new StringContent(requestBody, Encoding.UTF8, \"application/json\")\n};\n\n// ResponseHeadersRead로 실제 스트리밍을 활성화합니다 (전체 버퍼링 회피).\nusing var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);\nresponse.EnsureSuccessStatusCode();\nusing var stream = await response.Content.ReadAsStreamAsync();\nawait stream.CopyToAsync(ffplay.StandardInput.BaseStream);\nffplay.StandardInput.Close();\nawait ffplay.WaitForExitAsync();\n" }, { "lang": "Kotlin", "label": "Kotlin (OkHttp + ffplay)", "source": "// OkHttp 응답 스트림을 ffplay로 파이핑하여 실시간 재생.\n// 사전 설치: ffmpeg (brew/choco/apt install ffmpeg)\n// Android에서는 ffplay Process 대신 AudioTrack + 원시 PCM 전달을 사용하세요.\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.RequestBody.Companion.toRequestBody\n\nval ffplay = ProcessBuilder(\n \"ffplay\", \"-autoexit\", \"-nodisp\", \"-loglevel\", \"error\", \"-i\", \"pipe:0\"\n).redirectError(ProcessBuilder.Redirect.DISCARD).start()\n\nval client = OkHttpClient()\nval body = \"\"\"\n{\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\": \"문의해 주셔서 감사합니다. 금요일 오후 7시로 예약이 확정되었습니다.\",\n \"model\": \"ssfm-v30\"\n}\n\"\"\".trimIndent().toRequestBody(\"application/json\".toMediaType())\n\nval request = Request.Builder()\n .url(\"https://api.typecast.ai/v1/text-to-speech/stream\")\n .addHeader(\"X-API-KEY\", \"\")\n .post(body)\n .build()\n\nclient.newCall(request).execute().use { response ->\n response.body?.byteStream()?.use { input -> input.copyTo(ffplay.outputStream) }\n}\nffplay.outputStream.close()\nffplay.waitFor()\n" }, { "lang": "C++", "label": "C++ (libcurl + ffplay)", "source": "// 실시간 재생: libcurl write 콜백이 각 청크를 popen으로 연\n// ffplay 프로세스에 전달. 사전 설치: ffmpeg (brew/choco/apt install ffmpeg)\n#include \n#include \n#include \n\nstatic FILE* player = nullptr;\n\nsize_t cb(void* ptr, size_t size, size_t nmemb, void*) {\n return fwrite(ptr, size, nmemb, player);\n}\n\nint main() {\n player = popen(\"ffplay -autoexit -nodisp -loglevel error -i pipe:0\", \"w\");\n\n CURL* curl = curl_easy_init();\n struct curl_slist* headers = nullptr;\n headers = curl_slist_append(headers, \"Content-Type: application/json\");\n headers = curl_slist_append(headers, \"X-API-KEY: \");\n\n std::string body = R\"({\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\": \"문의해 주셔서 감사합니다. 금요일 오후 7시로 예약이 확정되었습니다.\",\n \"model\": \"ssfm-v30\"\n })\";\n\n curl_easy_setopt(curl, CURLOPT_URL, \"https://api.typecast.ai/v1/text-to-speech/stream\");\n curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);\n curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());\n curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cb);\n\n curl_easy_perform(curl);\n\n curl_slist_free_all(headers);\n curl_easy_cleanup(curl);\n pclose(player);\n return 0;\n}\n" }, { "lang": "C", "label": "C (libcurl + ffplay)", "source": "/* 실시간 재생: libcurl write 콜백이 각 청크를 popen으로 연\n * ffplay 프로세스에 전달. 사전 설치: ffmpeg (brew/choco/apt install ffmpeg) */\n#include \n#include \n\nstatic FILE* player = NULL;\n\nsize_t cb(void* ptr, size_t size, size_t nmemb, void* ud) {\n (void)ud;\n return fwrite(ptr, size, nmemb, player);\n}\n\nint main(void) {\n player = popen(\"ffplay -autoexit -nodisp -loglevel error -i pipe:0\", \"w\");\n\n curl_global_init(CURL_GLOBAL_ALL);\n CURL* curl = curl_easy_init();\n\n struct curl_slist* headers = NULL;\n headers = curl_slist_append(headers, \"Content-Type: application/json\");\n headers = curl_slist_append(headers, \"X-API-KEY: \");\n\n const char* body =\n \"{\"\n \"\\\"voice_id\\\":\\\"tc_60e5426de8b95f1d3000d7b5\\\",\"\n \"\\\"text\\\":\\\"문의해 주셔서 감사합니다. 금요일 오후 7시로 예약이 확정되었습니다.\\\",\"\n \"\\\"model\\\":\\\"ssfm-v30\\\"\"\n \"}\";\n\n curl_easy_setopt(curl, CURLOPT_URL, \"https://api.typecast.ai/v1/text-to-speech/stream\");\n curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);\n curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);\n curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cb);\n\n curl_easy_perform(curl);\n\n curl_slist_free_all(headers);\n curl_easy_cleanup(curl);\n curl_global_cleanup();\n pclose(player);\n return 0;\n}\n" }, { "lang": "Swift", "label": "Swift (URLSession + ffplay)", "source": "// 실시간 재생(macOS): URLSession 바이트 스트림을 Process로 연\n// ffplay에 파이핑. 사전 설치: ffmpeg (brew install ffmpeg).\n// URLSession.bytes(for:)는 iOS 15 / macOS 12 이상 필요.\n// 컴파일: swiftc -parse-as-library main.swift -o streaming_tts\n// iOS에서는 Process/ffplay 대신 AVAudioEngine + 스케줄드 PCM 버퍼 사용.\nimport Foundation\n\n@main\nstruct StreamingTTS {\n static func main() async throws {\n let ffplay = Process()\n ffplay.executableURL = URL(fileURLWithPath: \"/usr/bin/env\")\n ffplay.arguments = [\"ffplay\", \"-autoexit\", \"-nodisp\", \"-loglevel\", \"error\", \"-i\", \"pipe:0\"]\n let pipe = Pipe()\n ffplay.standardInput = pipe\n try ffplay.run()\n\n var request = URLRequest(url: URL(string: \"https://api.typecast.ai/v1/text-to-speech/stream\")!)\n request.httpMethod = \"POST\"\n request.setValue(\"application/json\", forHTTPHeaderField: \"Content-Type\")\n request.setValue(\"\", forHTTPHeaderField: \"X-API-KEY\")\n request.httpBody = try JSONSerialization.data(withJSONObject: [\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\": \"문의해 주셔서 감사합니다. 금요일 오후 7시로 예약이 확정되었습니다.\",\n \"model\": \"ssfm-v30\",\n ])\n\n let (bytes, _) = try await URLSession.shared.bytes(for: request)\n var buffer = Data()\n buffer.reserveCapacity(4096)\n for try await byte in bytes {\n buffer.append(byte)\n if buffer.count >= 4096 {\n try pipe.fileHandleForWriting.write(contentsOf: buffer)\n buffer.removeAll(keepingCapacity: true)\n }\n }\n if !buffer.isEmpty {\n try pipe.fileHandleForWriting.write(contentsOf: buffer)\n }\n try pipe.fileHandleForWriting.close()\n ffplay.waitUntilExit()\n }\n}\n" }, { "lang": "Rust", "label": "Rust (reqwest + ffplay)", "source": "// 실시간 재생: reqwest 스트림을 tokio Command로 연 ffplay에 파이핑.\n// 사전 설치: ffmpeg (brew/choco/apt install ffmpeg)\n// Cargo.toml:\n// reqwest = { version = \"0.12\", features = [\"json\", \"stream\"] }\n// tokio = { version = \"1\", features = [\"full\"] }\n// serde_json = \"1\"\nuse reqwest;\nuse serde_json::json;\nuse std::process::Stdio;\nuse tokio::io::AsyncWriteExt;\nuse tokio::process::Command;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box> {\n let mut ffplay = Command::new(\"ffplay\")\n .args([\"-autoexit\", \"-nodisp\", \"-loglevel\", \"error\", \"-i\", \"pipe:0\"])\n .stdin(Stdio::piped())\n .spawn()?;\n let mut stdin = ffplay.stdin.take().expect(\"failed to open ffplay stdin\");\n\n let client = reqwest::Client::new();\n let body = json!({\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\": \"문의해 주셔서 감사합니다. 금요일 오후 7시로 예약이 확정되었습니다.\",\n \"model\": \"ssfm-v30\"\n });\n\n let mut response = client\n .post(\"https://api.typecast.ai/v1/text-to-speech/stream\")\n .header(\"X-API-KEY\", \"\")\n .header(\"Content-Type\", \"application/json\")\n .json(&body)\n .send()\n .await?;\n\n while let Some(chunk) = response.chunk().await? {\n stdin.write_all(&chunk).await?;\n }\n drop(stdin);\n ffplay.wait().await?;\n Ok(())\n}\n" }, { "lang": "JavaScript", "label": "JavaScript (Node.js + ffplay)", "source": "// Node 18+ (내장 fetch). 스트림을 ffplay로 파이핑하여 실시간 재생.\n// 사전 설치: ffmpeg (brew/choco/apt install ffmpeg)\nimport { spawn } from \"node:child_process\";\n\nconst ffplay = spawn(\n \"ffplay\",\n [\"-autoexit\", \"-nodisp\", \"-loglevel\", \"error\", \"-i\", \"pipe:0\"],\n { stdio: [\"pipe\", \"ignore\", \"ignore\"] },\n);\n\nconst response = await fetch(\"https://api.typecast.ai/v1/text-to-speech/stream\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-KEY\": \"\",\n },\n body: JSON.stringify({\n voice_id: \"tc_60e5426de8b95f1d3000d7b5\",\n text: \"문의해 주셔서 감사합니다. 금요일 오후 7시로 예약이 확정되었습니다.\",\n model: \"ssfm-v30\",\n }),\n});\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\n\n// fetch().body는 Web ReadableStream — 청크가 도착하는 즉시 읽습니다.\nconst reader = response.body.getReader();\nwhile (true) {\n const { value, done } = await reader.read();\n if (done) break;\n ffplay.stdin.write(value);\n}\nffplay.stdin.end();\nawait new Promise((resolve) => ffplay.on(\"close\", resolve));\n" }, { "lang": "PHP", "label": "PHP (curl + ffplay)", "source": " \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\" => \"문의해 주셔서 감사합니다. 금요일 오후 7시로 예약이 확정되었습니다.\",\n \"model\" => \"ssfm-v30\",\n]);\n\n$ch = curl_init(\"https://api.typecast.ai/v1/text-to-speech/stream\");\ncurl_setopt_array($ch, [\n CURLOPT_POST => true,\n CURLOPT_HTTPHEADER => [\n \"Content-Type: application/json\",\n \"X-API-KEY: \",\n ],\n CURLOPT_POSTFIELDS => $payload,\n CURLOPT_WRITEFUNCTION => function ($ch, $data) use ($ffplay) {\n fwrite($ffplay, $data);\n return strlen($data);\n },\n]);\ncurl_exec($ch);\npclose($ffplay);\n" }, { "lang": "Go", "label": "Go (net/http + ffplay)", "source": "// 스트리밍 응답 본문을 ffplay stdin으로 파이핑.\n// 사전 설치: ffmpeg (brew/choco/apt install ffmpeg)\npackage main\n\nimport (\n \"bytes\"\n \"io\"\n \"net/http\"\n \"os/exec\"\n)\n\nfunc main() {\n ffplay := exec.Command(\"ffplay\", \"-autoexit\", \"-nodisp\", \"-loglevel\", \"error\", \"-i\", \"pipe:0\")\n stdin, _ := ffplay.StdinPipe()\n if err := ffplay.Start(); err != nil {\n panic(err)\n }\n\n body := []byte(`{\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\": \"문의해 주셔서 감사합니다. 금요일 오후 7시로 예약이 확정되었습니다.\",\n \"model\": \"ssfm-v30\"\n }`)\n\n req, _ := http.NewRequest(\"POST\", \"https://api.typecast.ai/v1/text-to-speech/stream\", bytes.NewReader(body))\n req.Header.Set(\"Content-Type\", \"application/json\")\n req.Header.Set(\"X-API-KEY\", \"\")\n\n resp, err := http.DefaultClient.Do(req)\n if err != nil {\n panic(err)\n }\n defer resp.Body.Close()\n\n io.Copy(stdin, resp.Body)\n stdin.Close()\n ffplay.Wait()\n}\n" }, { "lang": "Java", "label": "Java (HttpClient + ffplay)", "source": "// Java 11+ HttpClient + InputStream body handler.\n// 스트리밍 응답을 ffplay stdin으로 파이핑.\n// 사전 설치: ffmpeg (brew/choco/apt install ffmpeg)\nimport java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\nimport java.io.InputStream;\nimport java.io.OutputStream;\n\npublic class StreamingTTS {\n public static void main(String[] args) throws Exception {\n Process ffplay = new ProcessBuilder(\n \"ffplay\", \"-autoexit\", \"-nodisp\", \"-loglevel\", \"error\", \"-i\", \"pipe:0\")\n .redirectError(ProcessBuilder.Redirect.DISCARD)\n .start();\n\n String body = \"\"\"\n {\n \"voice_id\": \"tc_60e5426de8b95f1d3000d7b5\",\n \"text\": \"문의해 주셔서 감사합니다. 금요일 오후 7시로 예약이 확정되었습니다.\",\n \"model\": \"ssfm-v30\"\n }\n \"\"\";\n\n HttpRequest request = HttpRequest.newBuilder()\n .uri(URI.create(\"https://api.typecast.ai/v1/text-to-speech/stream\"))\n .header(\"Content-Type\", \"application/json\")\n .header(\"X-API-KEY\", \"\")\n .POST(HttpRequest.BodyPublishers.ofString(body))\n .build();\n\n HttpResponse response = HttpClient.newHttpClient()\n .send(request, HttpResponse.BodyHandlers.ofInputStream());\n\n try (InputStream in = response.body();\n OutputStream out = ffplay.getOutputStream()) {\n in.transferTo(out);\n }\n ffplay.waitFor();\n }\n}\n" }, { "lang": "Ruby", "label": "Ruby (net/http + ffplay)", "source": "# IO.popen으로 ffplay를 띄우고 스트리밍 응답을 파이핑.\n# 사전 설치: ffmpeg (brew/choco/apt install ffmpeg)\nrequire \"net/http\"\nrequire \"uri\"\nrequire \"json\"\n\nffplay = IO.popen(\n [\"ffplay\", \"-autoexit\", \"-nodisp\", \"-loglevel\", \"error\", \"-i\", \"pipe:0\"],\n \"wb\",\n)\n\nuri = URI(\"https://api.typecast.ai/v1/text-to-speech/stream\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nreq = Net::HTTP::Post.new(uri)\nreq[\"Content-Type\"] = \"application/json\"\nreq[\"X-API-KEY\"] = \"\"\nreq.body = {\n voice_id: \"tc_60e5426de8b95f1d3000d7b5\",\n text: \"문의해 주셔서 감사합니다. 금요일 오후 7시로 예약이 확정되었습니다.\",\n model: \"ssfm-v30\",\n}.to_json\n\nhttp.request(req) do |response|\n response.read_body { |chunk| ffplay.write(chunk) }\nend\n\nffplay.close\n" }, { "lang": "cURL", "label": "무음 제거", "source": "curl --no-buffer --request POST 'https://api.typecast.ai/v1/text-to-speech/stream' \\\n --header 'X-API-KEY: ' \\\n --header 'Content-Type: application/json' \\\n --output review.wav \\\n --data-binary @- <<'JSON'\n{\n \"voice_id\": \"\",\n \"text\": \"안녕하세요. 들어 주셔서 감사합니다.\",\n \"model\": \"ssfm-v30\",\n \"output\": {\n \"audio_format\": \"wav\",\n \"remove_silence_ms\": 300\n }\n}\nJSON\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "허용되는 최대 동시 요청 수" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "전체 커스텀 보이스 슬롯 한도 (professional 포함)" }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "사용 제한 정보" }, "Prompt": { "title": "프롬프트 (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "적용할 감정 프리셋.\r\n\r\nssfm-v21 지원 감정: normal, happy, sad, angry\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "example": 1, "description": "감정 표현 강도 제어(0.0~2.0).\r\n\r\n- 0.0: 완전히 중립적\r\n- 1.0: 표준 표현(기본값)\r\n- 2.0: 최대 강도\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "연령대 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **child**: 어린이 보이스(12세 미만)\n- **teenager**: 청소년 보이스(13-19세)\n- **young_adult**: 청년 보이스(20-35세)\n- **middle_age**: 중년 보이스(36-60세)\n- **elder**: 노년 보이스(60세 이상)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "플랜에서 기본으로 제공하는 총 크레딧" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "사용된 크레딧 수" } }, "description": "크레딧 사용 정보" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대 분류(어린이/청소년/청년/중년/노년)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별 분류(남성/여성)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "사용 가능한 감정이 있는 지원되는 TTS 모델 목록(예: [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유한 보이스 식별자. 기본 제공 보이스는 `tc_` prefix (예: `tc_60e5426de8b95f1d3000d7b5`), `POST /v1/voices/clone` 으로 만든 커스텀 보이스는 `uc_` prefix 를 사용합니다. 본인이 소유한 `uc_` 보이스도 `/v2/voices` 응답에 포함됩니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "이 보이스가 적합한 사용 사례 카테고리 목록\n\n## 보이스 이름 참고\n\n`voice_name` 필드는 영문으로만 반환됩니다. 아래 목록에서 한국어 표기를 확인하실 수 있습니다.\n\n
\n보이스 이름 목록 펼치기 (542개)\n\n| 영어 | 한국어 | 영어 | 한국어 | 영어 | 한국어 |\n| --- | --- | --- | --- | --- | --- |\n| Aaron | 에런 | Inhye | 인혜 | Piljae | 필재 |\n| Abigail | 아비게일 | Inseong | 인성 | Pyeonghwa | 평화 |\n| Ae-ran | 애란 | Instructor Han | 한유격 교관 | Rachel | 레이첼 |\n| Ael | 아엘 | Insun | 인선 | Ravi | 라비 |\n| Agatha | 아가사 | Jabbaba | 자바바 | Rayeon | 라연 |\n| Aiden | 에이든 | Jack | 잭 | Rebecca | 레베카 |\n| Alena | 알레나 | Jack-o’-Lantern | 잭오랜턴 | Reporter Bona | 다보나 기자 |\n| Alex | 알렉스 | Jackson | 잭슨 | Reporter Catalina | 카탈리나 기자 |\n| Amber | 엠버 | Jaeho | 재호 | Reporter Jinsub | 송진섭 기자 |\n| Anchor Hwa | 김경화 앵커 | Jaehun | 재훈 | Reporter Kang | 강수정 기자 |\n| Angela | 안젤라 | Jaejun | 재준 | Reporter Lee | 이승주 기자 |\n| Anja | 안냐 | Jaekyung | 재경 | Reporter Parkchan | 박찬 기자 |\n| Annabelle the Ghost | 귀신 애나벨 | Jaesun | 재선 | Rex | 렉스 |\n| Annie | 애니 | JaeYi | 재이 | Riley | 라일리 |\n| April | 에이프릴 | Jaeyoung | 재영 | Risan Ji | 지리산 |\n| Arang | 아랑 | Jain | 자인 | Rita | 리타 |\n| Ari | 아리 | Jake | 제이크 | River | 리버 |\n| Arin | 아린 | Janet | 재닛 | Robert | 로버트 |\n| Athena | 아테나 | Jangho | 장호 | Robo | 로보 |\n| Audrey | 오드리 | Jangwoon | 장운 | Romi | 로미 |\n| Avery | 에이버리 | Jechan | 제찬 | Ron | 론 |\n| Avong | 아봉 | Jeff | 제프 | Roro | 로로 |\n| Azzi | 아찌 | Jenna | 제나 | Rowoon | 로운 |\n| Babilon | 바빌론 | Jennifer | 제니퍼 | Royce | 로이스 |\n| Bell | 벨 | Jeong Choi | 최정 | Rudolph | 루돌프 |\n| Ben | 벤 | Jeongah | 정아 | Ruri | 루리 |\n| Benny | 베니 | JeongJin | 정진 | Rusty | 러스티 |\n| Beri | 베리 | Jeongseob | 정섭 | Ryan | 라이언 |\n| Billie | 빌리 | Jerome | 제롬 | Ryueun | 류은 |\n| Bomi | 이보미 | Jewby | 쥬비 | Sabrina the Witch | 마녀 사브리나 |\n| Bonggyu | 봉규 | Jian | 지안 | Saeron | 새론 |\n| Bongman Kim | 김봉만 | Jicheol | 지철 | Salty Chan-gu | 삐뚤어진 찬구 |\n| Bono | 보노 | Jihee | 지희 | Samantha | 사만다 |\n| Bora | 보라 | Jihoon | 지훈 | Sammy | 새미 |\n| Brad | 브래드 | Jihyun | 지현 | Sangdo | 상도 |\n| Bruce | 브루스 | Jimmy | 지미 | Sanghoon | 상훈 |\n| Buddy | 버디 | Jin | 진 | Sangwoo | 상우 |\n| Bumsoo | 범수 | Jinhan | 진한 | Santa | 산타 |\n| Buttaguy | 빠다가이 | Jinhang Kim | 김진행 | Santa Claus | 산타클로스 |\n| Caitlyn | 케이틀린 | Jinhee | 진희 | Santa Reporter | VJ 싼타 |\n| Callan | 칼란 | Jinhyuk | 진혁 | Sara | 세라 |\n| Camila | 카밀라 | Jinseo | 진서 | Sean | 션 |\n| Captain Bill | 캡틴 빌 | Jinung | 진웅 | SeHee | 세희 |\n| Carl | 칼 | Jinwoo | 진우 | Seheon | 세헌 |\n| Carlos | 카를로스 | Jiwoo | 지우 | Sejin | 세진 |\n| Carol | 캐롤 | Jiyoon | 지윤 | Sena | 세나 |\n| Catherine | 캐서린 | Jiyoung | 지영 | Seohee | 서희 |\n| Chad | 채드 | Jodie | 조디 | Seojin | 서진 |\n| Chaeah | 채아 | Jolly | 졸리 | Seojoon | 서준 |\n| Chan-gu | 찬구 | Jongdae | 종대 | Seok Choi | 최석 |\n| Changbae | 창배 | Jooeun | 주은 | SeokPil | 한석필 |\n| Changhee | 창희 | Joonghyun | 중현 | Seokpyo | 석표 |\n| Changmin | 창민 | Joongsik | 중식 | Seolhwa | 설화 |\n| Changsu | 박창수 | Joonkyu | 준규 | Seonha | 선하 |\n| Chanhyuk | 찬혁 | Joshua | 조슈아 | Seoyeon | 서연 |\n| Charlotte | 샬롯 | Juha | 주하 | Seoyoon | 서윤 |\n| Chase | 체이스 | Juho | 주호 | Seungah | 승아 |\n| Cheolhoon | 철훈 | Jungbong | 정봉 | Seungheon | 승헌 |\n| Cheolyong | 리철용 | Junghee | 정희 | Seungho | 승호 |\n| Cherry | 체리 | Jungjae | 정재 | Seunghwa | 승화 |\n| Chester | 체스터 | Jungmin | 정민 | Seungjae | 승재 |\n| Chiho | 치호 | Jungseok | 정석 | Seungmoon | 승문 |\n| Chloe | 클로이 | Jungsoon | 정순 | Seungwon | 승원 |\n| Choyeon | 강초연 교관 | Jungwon | 정원 | Seungyeon | 승연 |\n| Chungah | 청아 | Junhee | 준희 | Sewoo | 세우 |\n| Chunsik Kang | 강춘식 기자 | Junho | 준호 | Shana | 샤나 |\n| Claire | 클레어 | JunKi | 준기 | Sheriff Kim | 김반장 |\n| Classic Narrator | 미스타 변사 | Junsang | 준상 | Shinhe | 신혜 |\n| Cole | 콜 | Junseong | 준성 | Shinwook | 신욱 |\n| Cox | 콕스 | Justice Roh | 정의로 | Shotgun | 샷건 |\n| Cyrus | 사이러스 | Justin | 저스틴 | Simon | 사이먼 |\n| Dabin | 다빈 | Juwon | 주원 | Sindarin | 신다린 |\n| Daegil | 대길 | Juyoung | 주영 | Sio | 시오 |\n| Dahee | 다희 | K-Santa | 산타 할아버지 | Siwon | 시원 |\n| Dahyeon | 다현 | Kangil | 강일 | Siwoo | 시우 |\n| Damian | 데이미언 | Kanno | 칸노 | Siyeon | 시연 |\n| Dan | 댄 | Katie | 케이티 | Skylar | 스카일러 |\n| Dana | 다나 | Kelly | 켈리 | Slushy | 슬러시 |\n| Dasom | 다솜 | Kelsey | 켈시 | Smoke | 스모크 |\n| David | 데이비드 | Kevin | 케빈 | Sohye | 소혜 |\n| Dean | 딘 | Keybo | 키보 | Sojin | 소진 |\n| Deokhwan | 덕환 | Kijang Kim | 김기장 | Soobin | 수빈 |\n| Dohan | 도한 | Killian the Vampire | 뱀파이어 킬리언 | Sookhee | 숙희 |\n| Dohee | 도희 | Kiseob | 기섭 | Sooni | 순이 |\n| Dohyun | 도현 | Klip Kim | 클립 킴 | Sophia | 소피아 |\n| Dollar Jr. | 달러 주니어 | Koombo | 쿰보 | Sora | 소라 |\n| Doug | 더그 | Kristen | 크리스틴 | Soye | 소예 |\n| Doughnut | 도넛 | Kukhee | 국희 | Soyi | 소이 |\n| DU5T | 더스트 | Kwonil | 권일 | Soyoung | 소영 |\n| Duckchun | 덕춘 | Kyumin | 규민 | Soyul | 소율 |\n| Duke | 듀크 | Kyungho | 경호 | Spice | 스파이스 |\n| Dukgu | 덕구 | Kyungsoo | 경수 | Sportscaster Kang | 강수정 캐스터 |\n| Duman | 두만 | Kyungsook | 경숙 | Sportscaster Tony | 이영광 캐스터 |\n| Dupil | 곽두필 | Lady Cho | 단희빈 | Starling | 스탈링 |\n| DVZY | 데이지 | Lala | 라라 | Stephanie | 스테파니 |\n| Dylan | 딜런 | Lamie | 라미 | Storyteller Jinsub | 송진섭 스토리텔러 |\n| E-seller Sherri | 쇼린이 | Landon | 랜던 | Sua | 수아 |\n| Echo | 에코 | Larry | 래리 | Suho | 수호 |\n| Edward | 에드워드 | Leo | 레오 | Suji | 수지 |\n| Elias | 엘리아스 | Liam | 리암 | Sujin | 수진 |\n| Elise | 엘리스 | Lindsay | 린지 | Sullock Hong | 홍설록 |\n| Ella | 엘라 | Liz | 리즈 | Sumin | 수민 |\n| Eman | 이만 | Lloyd | 로이드 | Sungbae | 성배 |\n| Emma | 엠마 | Logan | 로건 | Sunggyu | 성규 |\n| Eunbin | 은빈 | Lucille | 루실 | Sungho | 성호 |\n| Eunchae | 은채 | Luna | 루나 | Sunghoon | 성훈 |\n| Eunha | 은하 | Lydia | 리디아 | Sunghyun | 성현 |\n| Eunsol | 은솔 | Maddie | 매디 | Sungjun | 홈쇼핑 성준 |\n| Frankenstein | 프랑켄슈타인 | Maisie | 메이시 | Sungkwon | 성권 |\n| Furnando | 터르난도 | Margaret | 마가렛 | Sungtae | 성태 |\n| Gaeul | 가을 | Margot | 마고 | Sungwook | 성욱 |\n| Gahee | 가희 | Matthew | 매튜 | Sunyoung | 선영 |\n| George | 조지 | MBTI EF (F) | MBTI EF 여 | Suyoon | 수윤 |\n| GeumHee | 금희 | MBTI EF (M) | MBTI EF 남 | Sylvia | 실비아 |\n| Geunhyeok | 근혁 | MBTI ET (F) | MBTI ET 여 | Taebaek | 장태백 |\n| Geunseok | 근석 | MBTI ET (M) | MBTI ET 남 | Taeji | 태지 |\n| Geunwoo | 근우 | MBTI IF (F) | MBTI IF 여 | Taejoong | 태중 |\n| Geunyeong | 근영 | MBTI IF (M) | MBTI IF 남 | Taemin | 태민 |\n| Ggami | 까미 | MBTI IT (F) | MBTI IT 여 | Taesub | 태섭 |\n| Ggomi | 꼬미 | MBTI IT (M) | MBTI IT 남 | Taewoo | 태우 |\n| Glenda | 글렌다 | MC Kong | MC콩 | Teal | 틸 |\n| Goat Kim | 김고트 | MC TypeCast | MC 타캐 | Tessa | 테사 |\n| Gongchul | 공철 | Mia | 미아 | Tian | 티안 |\n| Gowoon | 고운 | Michael | 마이클 | Tim | 팀 |\n| Grace | 그레이스 | Mija | 오미자 | Tina | 티나 |\n| Graham | 그레이엄 | Mijin | 미진 | Toby | 토비 |\n| Gunseok | 건석 | Mikyung | 미경 | Tommy | 토미 |\n| Gunwoo | 건우 | Millie | 밀리 | Tyson | 타이슨 |\n| Guri | 구리 | Minchae | 민채 | UiChan | 의찬이 |\n| Gus | 거스 | Minji | 민지 | Uncle Hank | 행크 |\n| Ha Eun | 하은 | Minjoon | 민준 | Valerie | 밸러리 |\n| Haejun | 해준 | Minju | 민주 | Valkyrie | 발키리 |\n| Haerang | 해랑 | Minjung | 민정 | Vanessa | 바네사 |\n| Hailey | 헤일리 | Minsang | 민상 | Verna | 베르나 |\n| Hajun | 하준 | Minsu | 민수 | Victoria | 빅토리아 |\n| Hakchul | 학철 | Mio | 미오 | Viktor | 빅토르 |\n| Hamchu | 햄쮸 | Miran Choi | 최미란 | Vincent | 빈센트 |\n| Han Taesung Caster | 한태성 캐스터 | Mirine | 미리내 | Vivien | 비비안 |\n| Hana | 하나 | Miseon | 미선 | Wade | 웨이드 |\n| Hangyeol | 한결 | Miso | 미소 | Walter | 월터 |\n| Hanjun | 한준 | Mister Gop | 미스터 갑 | Wangkwon | 왕권 |\n| Hanna | 한나 | Monggun | 몽군 | Weather Reporter Sky | 하늘 캐스터 |\n| Hans | 한스 | Moonjung | 문정 | West | 웨스트 |\n| Hansol | 한솔 | Mooyeol | 무열 | Wonho | 원호 |\n| Hanyoung | 한영 | Morgan | 모건 | Wonkyung | 원경 |\n| Harin | 하린 | Moru | 머루 | Wonwoo | 원우 |\n| Harper | 하퍼 | Mrs. Claus | 미세스 클로스 | Wooju | 우주 |\n| Hayul | 하율 | Munseok | 문석 | Woosung | 우성 |\n| Heejoon | 희준 | Munsu | 문수 | Xavier | 자비에르 |\n| Helena | 헬레나 | Myeonghee | 명희 | Yeeun | 예은 |\n| Henry | 헨리 | Myungil | 명일 | Yejin | 예진 |\n| Homun Sim | 심호문 | Myungjoo | 명주 | Yejoon | 예준 |\n| Hoon | 훈 | Naeun | 나은 | YeLin | 예린 |\n| Hosik | 호식 | Najin | 나진 | Yena | 예나 |\n| Hosun | 호선 | Namjoon | 남준 | Yeonah | 연아 |\n| Hoyoung | 호영 | Nana | 나나 | Yeonggeol | 영걸 |\n| Hugh | 휴 | Nari | 개나리 | Yeonhwa | 연화 |\n| Hwimin | 휘민 | Nathan | 네이선 | Yeonja | 연자 |\n| Hyejung | 혜정 | Neel | 닐 | Yeonsuh | 연서 |\n| Hyelee | 혜리 | Neoguard | 네오가드 | Yeonwoo | 연우 |\n| Hyemin | 혜민 | News anchor Jinsub | 송진섭 앵커 | Yeseul | 예슬 |\n| Hyena | 혜나 | Newscaster John | 존 앵커 | Yongsik | 용식이 |\n| Hyeongjin | 형진 | Nia | 니아 | Yoonseo | 윤서 |\n| Hyera | 혜라 | Noa | 노아 | Yoonsung | 윤성 |\n| Hyesu | 홈쇼핑 혜수 | Noel | 노엘 | Younggil | 영길 |\n| Hyewon | 혜원 | Noeul | 노을 | Younghee | 영희 |\n| Hyun | 현 | Norah | 노라 | Younghwan | 영환 |\n| Hyunji | 현지 | Nova | 노바 | Youngji | 영지 |\n| Hyunjin | 현진 | Old radio | 레디오 | Youngkyu | 영규 |\n| Hyunju | 현주 | Oliver | 올리버 | Youngmok | 영목 |\n| Hyunkyung | 현경 | Olivia | 올리비아 | Yubin | 유빈 |\n| Hyunmin | 현민 | Oscar | 오스카 | Yujin | 유진 |\n| Hyunseung | 현승 | Owen | 오언 | Yumi | 유미 |\n| HyunWoo | 현우 | P-0150N | 포이즌 | Yumin | 유민 |\n| Ian | 이안 | Pacang | 파캉 | Yunbin | 윤빈 |\n| Icarus | 이카루스 | Paige | 페이지 | Yunjeong | 윤정 |\n| Igyeom | 이겸 | Pangpang | 팡팡 | Yura | 유라 |\n| Ijun | 이준 | Patricia | 패트리샤 | Yuri | 유리 |\n| Ilho | 일호 | Patrick | 패트릭 | Yuseong | 유성 |\n| Ina | 이나 | Peter | 피터 | Zoey | 조이 |\n| Inhwa | 인화 | Philip | 필립 | | |\n\n
\n" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "사람이 읽을 수 있는 보이스 이름" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입 — `original` 은 타입캐스트가 기본 제공하는 스톡 보이스, `custom` 은 사용자가 업로드/클론한 보이스입니다." } }, "description": "모델별로 그룹화된 감정과 향상된 메타데이터가 있는 V2 보이스 응답 모델" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대입니다." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별(`male` 또는 `female`)입니다." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "지원하는 TTS 모델과 감정 목록입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유 보이스 식별자입니다. 기본 보이스는 `tc_`, 커스텀 보이스는 `uc_`로 시작합니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "추천 사용 사례 목록입니다." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "`{\"eng\": \"Daejin\", \"kor\": \"대진\"}` 같은 다국어 보이스 이름입니다." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입(`original` 또는 `custom`)입니다." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "제공되는 경우 사용할 수 있는 미리듣기 오디오 URL입니다." } }, "description": "다국어 이름과 미리듣기 URL을 포함한 보이스 정보입니다." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "API 플랜.\n\n사용 가능한 값:\n- **free**: 제한된 크레딧의 무료 등급\n- **lite**: 중간 수준 크레딧의 라이트 플랜\n- **plus**: 높은 크레딧의 플러스 플랜\n- **custom**: 커스텀 엔터프라이즈 플랜" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "음성 합성에 사용할 TTS 모델 버전. 다양한 모델은 다양한 기능과 품질 수준을 제공합니다.\n\n사용 가능한 모델:\n- **ssfm-v30**: 향상된 플로우와 추가 감정 프리셋이 있는 최신 모델(권장)\n- **ssfm-v21**: 검증된 신뢰성과 일관된 품질을 갖춘 안정적인 모델\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS 모델 버전(예: ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "이 모델에서 지원되는 감정 목록" } }, "description": "버전 및 지원되는 감정을 포함한 모델 정보" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "영어 보이스 이름입니다." }, "kor": { "type": "string", "title": "Kor", "description": "한국어 보이스 이름입니다." } }, "description": "ISO 639-3 언어 코드를 키로 사용하는 다국어 보이스 이름입니다." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "보이스 타입 분류.\n\n- `original` — 타입캐스트가 기본 제공하는 스톡 보이스로, 모든 계정에서 사용 가능합니다.\n- `custom` — 사용자가 본인의 샘플을 업로드하거나 클로닝해서 만든 보이스입니다." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "성별 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **male**: 남성 보이스\n- **female**: 여성 보이스\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "음성 합성에 사용 가능한 감정 프리셋. 각 감정은 생성된 음성의 톤, 속도, 표현력에 영향을 줍니다.\n\n**ssfm-v21 지원 감정 (4종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n\n**ssfm-v30 지원 감정 (7종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n- whisper: 부드럽고 조용한 말\n- toneup: 더 높은 톤 강조\n- tonedown: 더 낮은 톤 강조\n\n/v2/voices API 응답을 통해 각 음성에 사용 가능한 감정을 확인하세요.\n" }, "SmartPrompt": { "type": "object", "title": "스마트 프롬프트 (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "TTSRequest의 `text` 필드 이후에 오는 텍스트. 감정 추론을 위한 전방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 전환을 예측하는 데 도움\r\n- 다음 컨텍스트가 없으면 비워 둡니다\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "프롬프트 유형을 식별하는 판별자 필드. 컨텍스트 인식 감정 추론을 위해 \"smart\"로 설정해야 합니다.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "TTSRequest의 `text` 필드 이전에 오는 텍스트. 감정 추론을 위한 후방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 빌드업과 컨텍스트를 이해하는 데 도움\r\n- 이전 컨텍스트가 없으면 비워 둡니다\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "프리셋 프롬프트 (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "프롬프트 유형을 식별하는 판별자 필드. 프리셋 기반 감정 제어를 위해 \"preset\"으로 설정해야 합니다.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "생성된 음성에 적용할 감정 프리셋.\r\n\r\n지원되는 감정: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "생성된 음성의 감정 표현 강도를 제어합니다.\r\n\r\n- 0.0: 완전히 중립적, 감정 색채 없음\r\n- 0.5: 미묘한 감정 힌트\r\n- 1.0: 표준 감정 표현(기본값)\r\n- 1.5: 강한 감정 강조\r\n- 2.0: 최대 강도, 매우 표현력 있음\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "콘텐츠 유형 필터링을 위한 보이스 사용 사례 카테고리. 각 보이스는 특정 콘텐츠 유형에 대한 적합성을 나타내는 하나 이상의 사용 사례로 태그됩니다.\n\n**사용 가능한 사용 사례:**\n- **Announcer**: 공공 발표 및 프레젠테이션\n- **Anime**: 애니메이션 보이스\n- **Audiobook**: 장문 내레이션 및 스토리텔링\n- **Conversational**: 챗봇 및 대화형 AI\n- **Documentary**: 다큐멘터리 내레이션 및 해설\n- **E-learning**: 교육 콘텐츠 및 튜토리얼\n- **Rapper**: 랩 및 음악 퍼포먼스\n- **Game**: 비디오 게임 보이스 및 내레이션\n- **Tiktok/Reels**: SNS 숏츠\n- **News**: 뉴스 방송\n- **Podcast**: 방송 및 팟캐스트 제작\n- **Voicemail**: IVR 시스템 및 음성 비서\n- **Ads**: 광고 및 홍보 콘텐츠\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "문제를 설명하는 오류 메시지" }, "message": { "type": "string", "description": "구조화된 오류에 대한 설명" }, "error_code": { "type": "string", "description": "구조화된 오류를 식별하는 코드" } }, "description": "기존 오류와 유효성 검사 오류는 `detail`을 사용하며, 구조화된 오류는 `error_code`와 `message`를 사용합니다." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "`status`가 `failed`일 때의 실패 사유입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "source": { "type": "string", "title": "Source", "description": "생성 방식(`instant` 또는 `professional`)입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "UTC 기준 생성 시각입니다." } }, "description": "인증된 계정이 소유한 커스텀 보이스입니다." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "추천 관련도 점수. 값이 높을수록 검색어와 더 잘 맞는 후보입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "`tc_` prefix 가 붙은 타입캐스트 보이스 식별자. 텍스트 음성 변환 요청의 `voice_id` 로 사용할 수 있습니다." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "사람이 읽을 수 있는 보이스 이름." } }, "description": "`GET /v1/voices/recommendations` 응답의 보이스 추천 후보. 관련도 순서로 정렬됩니다." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "커스텀 보이스의 현재 생성 또는 학습 상태입니다." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름 (1~30자)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "보이스가 클로닝된 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "`uc_` prefix 가 붙은 커스텀 보이스 식별자. `POST /v1/text-to-speech` 등 `voice_id` 를 받는 엔드포인트에 그대로 사용할 수 있습니다." } }, "description": "`POST /v1/voices/clone` 응답 — 퀵 클로닝으로 생성된 커스텀 보이스 메타데이터." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "세그먼트 구분값입니다. 항상 `pause`입니다." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "초 단위 쉼 길이입니다. 개별 쉼은 최대 10초, 모든 쉼의 합은 최대 60초입니다.", "exclusiveMinimum": 0 } }, "description": "크레딧 차감 없이 삽입되는 무음 구간입니다.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 단어 단위 정렬 구간." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "현재 구독 플랜명" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "사용 제한 정보" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "크레딧 사용 정보" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "구독 정보 응답 모델" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호/공백 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 문자 단위 정렬 구간." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." } }, "description": "퀵 클로닝 또는 프리미엄 클로닝 생성 요청 결과입니다." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "base64 로 인코딩된 오디오 바이트. `audio_format` 확장자로 디코딩해 파일로 저장할 수 있습니다." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "단어 단위 타임스탬프(문장부호 포함). 요청이 `granularity=char` 일 때는 `null`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "문자 단위 타임스탬프(문장부호와 공백 포함). 요청이 `granularity=word` 일 때는 `null`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "`audio` 필드의 오디오 인코딩 포맷 — `wav` 또는 `mp3` (요청의 `output.audio_format` 에 따라 결정)." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "생성된 오디오의 길이(초)." } }, "description": "TTS 생성 + 타임스탬프 정렬 통합 응답." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "오디오 샘플. WAV 또는 MP3, 최대 25MB, 5초 이상 150초 이하.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "보이스 이름 (1~30자)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "클로닝할 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." } }, "description": "퀵 클로닝 요청을 위한 multipart 본문." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "파일", "format": "binary", "description": "25 MiB 이하이면서 길이가 5초 이상 150초 이하인 WAV 또는 MP3 음성 파일 1개입니다.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "상품 내레이터", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." } }, "description": "퀵 클로닝용 multipart 요청 본문입니다." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "커스텀 보이스 이름", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "파일", "description": "학습에 사용할 WAV 또는 MP3 음성 파일입니다. 하나의 파일을 업로드할 수 있습니다.\r\n\r\n**업로드 제한**\r\n\r\n* WAV 또는 MP3 파일 1개\r\n* 파일 크기: 1 GiB 이하\r\n* 길이: 5분 이상 3시간 이하\r\n* 샘플레이트: 16 kHz 이상\r\n\r\n최상의 결과를 위해 다음 조건을 충족하는 오디오를 권장합니다.\r\n\r\n* 생성된 보이스가 재현하기를 원하는 말투와 최대한 가깝게 녹음해 주세요.\r\n* 배경 소음이 없는 조용한 환경에서 녹음해 주세요.\r\n* 한 명의 화자 목소리만 포함해 주세요.\r\n* `language` 필드에 지정한 언어로 녹음해 주세요.\r\n* 오디오 길이가 길수록 생성되는 보이스의 품질이 향상됩니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." }, "language": { "type": "string", "title": "Language", "example": "kor", "description": "`kor`, `eng` 같은 ISO 639-3 언어 코드입니다." } }, "description": "프리미엄 클로닝용 multipart 요청 본문입니다." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "인증을 위한 API 키. 타입캐스트 API 콘솔에서 API 키를 생성할 수 있습니다." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 조합형 텍스트 음성 변환(Compose TTS) > 여러 음성 구간과 쉼을 하나의 오디오 파일로 생성합니다. `segments` 배열에 `tts`와 `pause` 객체를 재생할 순서대로 넣으세요. 각 `tts` 세그먼트는 `POST /v1/text-to-speech`와 동일한 보이스, 모델, 감정, 출력 설정을 지원하며 세그먼트마다 다른 보이스와 모델을 사용할 수 있습니다. **제한** - 전체 세그먼트는 최대 50개이며 `tts` 세그먼트가 최소 1개 필요합니다. - 모든 `tts` 세그먼트의 텍스트 합은 최대 2,000자입니다. - 쉼은 개별 최대 10초, 전체 합계 최대 60초입니다. - 모든 `tts` 세그먼트의 `audio_format`은 같아야 합니다. 크레딧은 `tts` 텍스트 길이의 합만큼 차감되며 쉼에는 차감되지 않습니다. 세그먼트는 병렬로 합성된 뒤 입력 순서대로 반환됩니다. 하나라도 합성에 실패하면 전체 요청이 실패하며 크레딧은 차감되지 않습니다. **응답** 성공하면 JSON이 아닌 합성된 오디오 바이너리 데이터를 직접 반환합니다. 응답의 `Content-Type`은 세그먼트에서 요청한 `audio_format`에 따라 `audio/wav` 또는 `audio/mpeg`입니다. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "프로덕션 서버" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/text-to-speech/compose": { "post": { "tags": [ "Text-to-Speech" ], "x-mint": { "href": "/ko/api-reference/text-to-speech/compose-text-to-speech" }, "summary": "조합형 텍스트 음성 변환(Compose TTS)", "security": [ { "ApiKeyAuth": [] } ], "responses": { "200": { "content": { "audio/wav": { "schema": { "type": "string", "format": "binary" }, "example": "[Binary audio data - WAV file content]" } }, "description": "요청한 `audio_format`에 맞는 바이너리 합성 오디오 데이터" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "인증 실패" }, "402": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Insufficient credit" } } }, "description": "전체 텍스트 길이에 비해 크레딧이 부족한 경우" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" } } }, "description": "세그먼트가 올바르지 않거나 Compose 제한을 초과했거나 입력 텍스트를 합성할 수 없는 경우" }, "500": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "An unexpected error occurred" } } }, "description": "하나 이상의 세그먼트를 합성하는 중 예상하지 못한 서버 오류가 발생한 경우" } }, "deprecated": false, "description": "여러 음성 구간과 쉼을 하나의 오디오 파일로 생성합니다. `segments` 배열에 `tts`와 `pause` 객체를 재생할 순서대로 넣으세요. 각 `tts` 세그먼트는 `POST /v1/text-to-speech`와 동일한 보이스, 모델, 감정, 출력 설정을 지원하며 세그먼트마다 다른 보이스와 모델을 사용할 수 있습니다.\n\n**제한**\n- 전체 세그먼트는 최대 50개이며 `tts` 세그먼트가 최소 1개 필요합니다.\n- 모든 `tts` 세그먼트의 텍스트 합은 최대 2,000자입니다.\n- 쉼은 개별 최대 10초, 전체 합계 최대 60초입니다.\n- 모든 `tts` 세그먼트의 `audio_format`은 같아야 합니다.\n\n크레딧은 `tts` 텍스트 길이의 합만큼 차감되며 쉼에는 차감되지 않습니다. 세그먼트는 병렬로 합성된 뒤 입력 순서대로 반환됩니다. 하나라도 합성에 실패하면 전체 요청이 실패하며 크레딧은 차감되지 않습니다.\n\n**응답**\n성공하면 JSON이 아닌 합성된 오디오 바이너리 데이터를 직접 반환합니다. 응답의 `Content-Type`은 세그먼트에서 요청한 `audio_format`에 따라 `audio/wav` 또는 `audio/mpeg`입니다.", "operationId": "text_to_speech_compose_v1_text_to_speech_compose_post", "requestBody": { "content": { "application/json": { "schema": { "type": "object", "title": "ComposeRequest", "required": [ "segments" ], "properties": { "segments": { "type": "array", "items": { "oneOf": [ { "type": "object", "title": "TTSComposeSegment", "examples": [ { "text": "안녕하세요. 오늘의 소식입니다.", "type": "tts", "model": "ssfm-v30", "output": { "audio_format": "wav" }, "language": "kor", "voice_id": "tc_672c5f5ce59fac2a48faeaee" } ], "required": [ "type", "voice_id", "text", "model" ], "properties": { "seed": { "anyOf": [ { "type": "integer", "maximum": 4294967295, "minimum": 0 }, { "type": "null" } ], "title": "Seed", "example": 42, "description": "합성 결과 재현에 사용할 선택적 부호 없는 정수 시드입니다." }, "text": { "type": "string", "title": "Text", "example": "안녕하세요. 오늘의 소식입니다.", "maxLength": 2000, "minLength": 1, "description": "합성할 텍스트입니다. 모든 `tts` 세그먼트의 텍스트 합은 최대 2,000자입니다." }, "type": { "type": "string", "const": "tts", "title": "Type", "default": "tts", "description": "세그먼트 구분값입니다. 항상 `tts`입니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "이 세그먼트에 사용할 음성 모델입니다. 세그먼트마다 다른 모델을 사용할 수 있습니다." }, "output": { "type": "object", "title": "Output", "properties": { "volume": { "anyOf": [ { "type": "integer", "maximum": 200, "minimum": 0 }, { "type": "null" } ], "title": "Volume", "example": 100, "description": "출력 음성의 상대적인 음량 조절: 0(완전 무음), 50(절반 볼륨), 100(표준 볼륨, 기본값), 150(표준보다 50% 크게), 200(최대 볼륨, 표준의 두 배).\r\n\r\n출력된 음성마다 음량이 다를 경우, 단순 비율 조절인 `volume`을 사용하면 음성 간의 음량 편차가 더욱 커질 수 있습니다. 일정한 음량 출력이 필요한 경우 `target_lufs` 사용을 권장합니다.\r\n\r\n- **주의:** `target_lufs`와 동시에 사용할 수 없습니다.\r\n\r\n필수 범위: 0 <= x <= 200\r\n" }, "audio_pitch": { "type": "integer", "title": "Audio Pitch", "default": 0, "example": 0, "maximum": 12, "minimum": -12, "description": "성별과 나이에 영향을 주는 반음 단위의 피치 조정: -12(한 옥타브 낮게, 더 깊은 목소리), -6(반 옥타브 낮게), 0(원래 피치, 기본값), +6(반 옥타브 높게), +12(한 옥타브 높게, 더 높은 목소리)" }, "audio_tempo": { "type": "number", "title": "Audio Tempo", "default": 1, "example": 1, "maximum": 2, "minimum": 0.5, "description": "음성 속도 제어: 0.5(절반 속도, 매우 느리고 명확함), 0.75(보통보다 약간 느림), 1.0(보통 말하기 속도, 기본값), 1.5(보통보다 50% 빠름), 2.0(두 배 속도, 매우 빠른 음성)" }, "target_lufs": { "type": "integer", "anyOf": [ { "type": "number", "maximum": 0, "minimum": -70 }, { "type": "null" } ], "title": "Target Lufs", "example": -14, "description": "출력 음성의 목표 절대 음량(LUFS) 설정. 원본 음성의 크기와 상관없이 모든 음성을 일정한 크기로 정규화하여 생성합니다. 값이 0에 가까울수록 소리가 커지며, -70에 가까울수록 작아집니다.\r\n\r\n- 필수 범위: -70 <= x <= 0\r\n- 권장값: -14 (일반적인 스트리밍 표준), -23 (방송 표준)\r\n- **주의:** `volume` 파라미터와 함께 사용할 수 없습니다. 절대적인 음량 기준이 필요할 때는 `target_lufs`를, 상대적인 비율 조절이 필요할 때는 `volume`을 선택하여 사용하세요.\r\n" }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "default": "wav", "example": "wav", "description": "출력 오디오 형식.\r\n\r\n**WAV 형식:**\r\n- 비압축 PCM 오디오\r\n- 16비트 깊이, 모노 채널, 44100 Hz 샘플링 속도\r\n- 더 높은 품질, 더 큰 파일 크기\r\n- 전문 오디오 제작에 권장\r\n\r\n**MP3 형식:**\r\n- 압축된 MPEG Layer III 오디오\r\n- 320 kbps 비트레이트, 44100 Hz 샘플링 속도\r\n- 더 작은 파일 크기\r\n- 웹 스트리밍 및 배포에 권장\r\n" }, "remove_silence_ms": { "anyOf": [ { "type": "integer", "maximum": 1000, "minimum": 0 }, { "type": "null" } ], "title": "무음 구간 길이 (ms)", "default": null, "example": 100, "description": "무음 제거를 적용하면 음성 내 무음 구간 중 지정한 길이보다 긴 구간을 해당 길이로 줄입니다. 단위는 밀리초(ms)입니다. 지정값은 제거할 시간이 아니라 남길 무음 길이입니다.\r\n\r\n**입력값:**\r\n- **0부터 1000까지**의 정수, 권장 범위는 0~200.\r\n- 생략 또는 `null`: 무음 제거를 적용하지 않습니다.\r\n- `0`: 0ms 초과의 무음을 제거합니다. **기능을 끄는 값이 아닙니다.**\r\n- 불리언·문자열·소수·범위 밖 값은 허용하지 않습니다.\r\n\r\n**예시:** `100`을 지정하면 음성 내 무음 구간 중 100ms보다 긴 구간을 100ms로 줄입니다. \r\n값이 작을수록 더 짧은 무음 구간까지 제거 대상에 포함되고, 각 구간에서 남기는 무음도 짧아집니다." } }, "description": "이 세그먼트의 오디오 설정입니다. 모든 세그먼트에서 같은 `audio_format`을 사용해야 합니다.\r\n\r\n`remove_silence_ms`(정수, 0~1000ms)로 검출된 무음 구간을 줄일 수 있습니다." }, "prompt": { "oneOf": [ { "$ref": "#/components/schemas/SmartPrompt" }, { "$ref": "#/components/schemas/PresetPrompt" }, { "$ref": "#/components/schemas/Prompt" } ], "title": "Prompt", "description": "이 세그먼트의 감정과 문맥 설정입니다." }, "language": { "type": "string", "title": "Language", "example": "kor", "description": "ISO 639-3 언어 코드입니다. 생략하면 텍스트에서 언어를 감지합니다." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_672c5f5ce59fac2a48faeaee", "description": "타입캐스트 기본 보이스(`tc_`) 또는 커스텀 보이스(`uc_`) 식별자입니다." } }, "description": "일반 텍스트 음성 변환 요청과 동일한 합성 옵션을 사용하는 음성 세그먼트입니다." }, { "$ref": "#/components/schemas/PauseComposeSegment" } ], "discriminator": { "mapping": { "tts": "#/components/schemas/TTSComposeSegment", "pause": "#/components/schemas/PauseComposeSegment" }, "propertyName": "type" } }, "title": "Segments", "maxItems": 50, "minItems": 1, "description": "출력 순서대로 나열한 음성과 쉼 세그먼트입니다. 최소 1개, 최대 50개이며 `tts` 세그먼트가 적어도 하나 필요합니다." } }, "description": "음성과 쉼 세그먼트를 순서대로 합성하여 하나의 오디오 파일로 반환하는 요청입니다." }, "example": { "segments": [ { "text": "안녕하세요. 오늘의 소식입니다.", "type": "tts", "model": "ssfm-v30", "output": { "audio_format": "wav" }, "language": "kor", "voice_id": "tc_672c5f5ce59fac2a48faeaee" }, { "type": "pause", "duration_seconds": 1.5 }, { "text": "첫 번째 소식을 전해드립니다.", "type": "tts", "model": "ssfm-v30", "output": { "audio_format": "wav" }, "language": "kor", "voice_id": "tc_66aca22c7d31e45ff05ff418" } ] } } }, "required": true, "description": "" }, "x-codeSamples": [ { "lang": "cURL", "label": "cURL (파일로 저장)", "source": "curl --request POST \\\n --url https://api.typecast.ai/v1/text-to-speech/compose \\\n --header 'Content-Type: application/json' \\\n --header 'X-API-KEY: ' \\\n --output output.wav \\\n --data @- <\"},\n json={\n \"segments\": [\n {\n \"type\": \"tts\",\n \"voice_id\": \"tc_672c5f5ce59fac2a48faeaee\",\n \"text\": \"안녕하세요. 오늘의 소식입니다.\",\n \"model\": \"ssfm-v30\",\n \"language\": \"kor\",\n \"output\": {\"audio_format\": \"wav\"},\n },\n {\"type\": \"pause\", \"duration_seconds\": 1.5},\n {\n \"type\": \"tts\",\n \"voice_id\": \"tc_66aca22c7d31e45ff05ff418\",\n \"text\": \"첫 번째 소식을 전해드립니다.\",\n \"model\": \"ssfm-v30\",\n \"language\": \"kor\",\n \"output\": {\"audio_format\": \"wav\"},\n },\n ]\n },\n timeout=120,\n)\nresponse.raise_for_status()\nwith open(\"output.wav\", \"wb\") as audio_file:\n audio_file.write(response.content)\n" }, { "lang": "cURL", "label": "무음 제거", "source": "curl --request POST 'https://api.typecast.ai/v1/text-to-speech/compose' \\\n --header 'X-API-KEY: ' \\\n --header 'Content-Type: application/json' \\\n --output review.wav \\\n --data-binary @- <<'JSON'\n{\n \"segments\": [\n {\n \"type\": \"tts\",\n \"voice_id\": \"\",\n \"text\": \"안녕하세요. 들어 주셔서 감사합니다.\",\n \"model\": \"ssfm-v30\",\n \"output\": {\n \"audio_format\": \"wav\",\n \"remove_silence_ms\": 300\n }\n },\n {\n \"type\": \"pause\",\n \"duration_seconds\": 1.5\n },\n {\n \"type\": \"tts\",\n \"voice_id\": \"\",\n \"text\": \"안녕하세요. 들어 주셔서 감사합니다.\",\n \"model\": \"ssfm-v30\",\n \"output\": {\n \"audio_format\": \"wav\",\n \"remove_silence_ms\": 300\n }\n }\n ]\n}\nJSON\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "허용되는 최대 동시 요청 수" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "전체 커스텀 보이스 슬롯 한도 (professional 포함)" }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "사용 제한 정보" }, "Prompt": { "title": "프롬프트 (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "적용할 감정 프리셋.\r\n\r\nssfm-v21 지원 감정: normal, happy, sad, angry\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "example": 1, "description": "감정 표현 강도 제어(0.0~2.0).\r\n\r\n- 0.0: 완전히 중립적\r\n- 1.0: 표준 표현(기본값)\r\n- 2.0: 최대 강도\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "연령대 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **child**: 어린이 보이스(12세 미만)\n- **teenager**: 청소년 보이스(13-19세)\n- **young_adult**: 청년 보이스(20-35세)\n- **middle_age**: 중년 보이스(36-60세)\n- **elder**: 노년 보이스(60세 이상)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "플랜에서 기본으로 제공하는 총 크레딧" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "사용된 크레딧 수" } }, "description": "크레딧 사용 정보" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대 분류(어린이/청소년/청년/중년/노년)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별 분류(남성/여성)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "사용 가능한 감정이 있는 지원되는 TTS 모델 목록(예: [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유한 보이스 식별자. 기본 제공 보이스는 `tc_` prefix (예: `tc_60e5426de8b95f1d3000d7b5`), `POST /v1/voices/clone` 으로 만든 커스텀 보이스는 `uc_` prefix 를 사용합니다. 본인이 소유한 `uc_` 보이스도 `/v2/voices` 응답에 포함됩니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "이 보이스가 적합한 사용 사례 카테고리 목록\n\n## 보이스 이름 참고\n\n`voice_name` 필드는 영문으로만 반환됩니다. 아래 목록에서 한국어 표기를 확인하실 수 있습니다.\n\n
\n보이스 이름 목록 펼치기 (542개)\n\n| 영어 | 한국어 | 영어 | 한국어 | 영어 | 한국어 |\n| --- | --- | --- | --- | --- | --- |\n| Aaron | 에런 | Inhye | 인혜 | Piljae | 필재 |\n| Abigail | 아비게일 | Inseong | 인성 | Pyeonghwa | 평화 |\n| Ae-ran | 애란 | Instructor Han | 한유격 교관 | Rachel | 레이첼 |\n| Ael | 아엘 | Insun | 인선 | Ravi | 라비 |\n| Agatha | 아가사 | Jabbaba | 자바바 | Rayeon | 라연 |\n| Aiden | 에이든 | Jack | 잭 | Rebecca | 레베카 |\n| Alena | 알레나 | Jack-o’-Lantern | 잭오랜턴 | Reporter Bona | 다보나 기자 |\n| Alex | 알렉스 | Jackson | 잭슨 | Reporter Catalina | 카탈리나 기자 |\n| Amber | 엠버 | Jaeho | 재호 | Reporter Jinsub | 송진섭 기자 |\n| Anchor Hwa | 김경화 앵커 | Jaehun | 재훈 | Reporter Kang | 강수정 기자 |\n| Angela | 안젤라 | Jaejun | 재준 | Reporter Lee | 이승주 기자 |\n| Anja | 안냐 | Jaekyung | 재경 | Reporter Parkchan | 박찬 기자 |\n| Annabelle the Ghost | 귀신 애나벨 | Jaesun | 재선 | Rex | 렉스 |\n| Annie | 애니 | JaeYi | 재이 | Riley | 라일리 |\n| April | 에이프릴 | Jaeyoung | 재영 | Risan Ji | 지리산 |\n| Arang | 아랑 | Jain | 자인 | Rita | 리타 |\n| Ari | 아리 | Jake | 제이크 | River | 리버 |\n| Arin | 아린 | Janet | 재닛 | Robert | 로버트 |\n| Athena | 아테나 | Jangho | 장호 | Robo | 로보 |\n| Audrey | 오드리 | Jangwoon | 장운 | Romi | 로미 |\n| Avery | 에이버리 | Jechan | 제찬 | Ron | 론 |\n| Avong | 아봉 | Jeff | 제프 | Roro | 로로 |\n| Azzi | 아찌 | Jenna | 제나 | Rowoon | 로운 |\n| Babilon | 바빌론 | Jennifer | 제니퍼 | Royce | 로이스 |\n| Bell | 벨 | Jeong Choi | 최정 | Rudolph | 루돌프 |\n| Ben | 벤 | Jeongah | 정아 | Ruri | 루리 |\n| Benny | 베니 | JeongJin | 정진 | Rusty | 러스티 |\n| Beri | 베리 | Jeongseob | 정섭 | Ryan | 라이언 |\n| Billie | 빌리 | Jerome | 제롬 | Ryueun | 류은 |\n| Bomi | 이보미 | Jewby | 쥬비 | Sabrina the Witch | 마녀 사브리나 |\n| Bonggyu | 봉규 | Jian | 지안 | Saeron | 새론 |\n| Bongman Kim | 김봉만 | Jicheol | 지철 | Salty Chan-gu | 삐뚤어진 찬구 |\n| Bono | 보노 | Jihee | 지희 | Samantha | 사만다 |\n| Bora | 보라 | Jihoon | 지훈 | Sammy | 새미 |\n| Brad | 브래드 | Jihyun | 지현 | Sangdo | 상도 |\n| Bruce | 브루스 | Jimmy | 지미 | Sanghoon | 상훈 |\n| Buddy | 버디 | Jin | 진 | Sangwoo | 상우 |\n| Bumsoo | 범수 | Jinhan | 진한 | Santa | 산타 |\n| Buttaguy | 빠다가이 | Jinhang Kim | 김진행 | Santa Claus | 산타클로스 |\n| Caitlyn | 케이틀린 | Jinhee | 진희 | Santa Reporter | VJ 싼타 |\n| Callan | 칼란 | Jinhyuk | 진혁 | Sara | 세라 |\n| Camila | 카밀라 | Jinseo | 진서 | Sean | 션 |\n| Captain Bill | 캡틴 빌 | Jinung | 진웅 | SeHee | 세희 |\n| Carl | 칼 | Jinwoo | 진우 | Seheon | 세헌 |\n| Carlos | 카를로스 | Jiwoo | 지우 | Sejin | 세진 |\n| Carol | 캐롤 | Jiyoon | 지윤 | Sena | 세나 |\n| Catherine | 캐서린 | Jiyoung | 지영 | Seohee | 서희 |\n| Chad | 채드 | Jodie | 조디 | Seojin | 서진 |\n| Chaeah | 채아 | Jolly | 졸리 | Seojoon | 서준 |\n| Chan-gu | 찬구 | Jongdae | 종대 | Seok Choi | 최석 |\n| Changbae | 창배 | Jooeun | 주은 | SeokPil | 한석필 |\n| Changhee | 창희 | Joonghyun | 중현 | Seokpyo | 석표 |\n| Changmin | 창민 | Joongsik | 중식 | Seolhwa | 설화 |\n| Changsu | 박창수 | Joonkyu | 준규 | Seonha | 선하 |\n| Chanhyuk | 찬혁 | Joshua | 조슈아 | Seoyeon | 서연 |\n| Charlotte | 샬롯 | Juha | 주하 | Seoyoon | 서윤 |\n| Chase | 체이스 | Juho | 주호 | Seungah | 승아 |\n| Cheolhoon | 철훈 | Jungbong | 정봉 | Seungheon | 승헌 |\n| Cheolyong | 리철용 | Junghee | 정희 | Seungho | 승호 |\n| Cherry | 체리 | Jungjae | 정재 | Seunghwa | 승화 |\n| Chester | 체스터 | Jungmin | 정민 | Seungjae | 승재 |\n| Chiho | 치호 | Jungseok | 정석 | Seungmoon | 승문 |\n| Chloe | 클로이 | Jungsoon | 정순 | Seungwon | 승원 |\n| Choyeon | 강초연 교관 | Jungwon | 정원 | Seungyeon | 승연 |\n| Chungah | 청아 | Junhee | 준희 | Sewoo | 세우 |\n| Chunsik Kang | 강춘식 기자 | Junho | 준호 | Shana | 샤나 |\n| Claire | 클레어 | JunKi | 준기 | Sheriff Kim | 김반장 |\n| Classic Narrator | 미스타 변사 | Junsang | 준상 | Shinhe | 신혜 |\n| Cole | 콜 | Junseong | 준성 | Shinwook | 신욱 |\n| Cox | 콕스 | Justice Roh | 정의로 | Shotgun | 샷건 |\n| Cyrus | 사이러스 | Justin | 저스틴 | Simon | 사이먼 |\n| Dabin | 다빈 | Juwon | 주원 | Sindarin | 신다린 |\n| Daegil | 대길 | Juyoung | 주영 | Sio | 시오 |\n| Dahee | 다희 | K-Santa | 산타 할아버지 | Siwon | 시원 |\n| Dahyeon | 다현 | Kangil | 강일 | Siwoo | 시우 |\n| Damian | 데이미언 | Kanno | 칸노 | Siyeon | 시연 |\n| Dan | 댄 | Katie | 케이티 | Skylar | 스카일러 |\n| Dana | 다나 | Kelly | 켈리 | Slushy | 슬러시 |\n| Dasom | 다솜 | Kelsey | 켈시 | Smoke | 스모크 |\n| David | 데이비드 | Kevin | 케빈 | Sohye | 소혜 |\n| Dean | 딘 | Keybo | 키보 | Sojin | 소진 |\n| Deokhwan | 덕환 | Kijang Kim | 김기장 | Soobin | 수빈 |\n| Dohan | 도한 | Killian the Vampire | 뱀파이어 킬리언 | Sookhee | 숙희 |\n| Dohee | 도희 | Kiseob | 기섭 | Sooni | 순이 |\n| Dohyun | 도현 | Klip Kim | 클립 킴 | Sophia | 소피아 |\n| Dollar Jr. | 달러 주니어 | Koombo | 쿰보 | Sora | 소라 |\n| Doug | 더그 | Kristen | 크리스틴 | Soye | 소예 |\n| Doughnut | 도넛 | Kukhee | 국희 | Soyi | 소이 |\n| DU5T | 더스트 | Kwonil | 권일 | Soyoung | 소영 |\n| Duckchun | 덕춘 | Kyumin | 규민 | Soyul | 소율 |\n| Duke | 듀크 | Kyungho | 경호 | Spice | 스파이스 |\n| Dukgu | 덕구 | Kyungsoo | 경수 | Sportscaster Kang | 강수정 캐스터 |\n| Duman | 두만 | Kyungsook | 경숙 | Sportscaster Tony | 이영광 캐스터 |\n| Dupil | 곽두필 | Lady Cho | 단희빈 | Starling | 스탈링 |\n| DVZY | 데이지 | Lala | 라라 | Stephanie | 스테파니 |\n| Dylan | 딜런 | Lamie | 라미 | Storyteller Jinsub | 송진섭 스토리텔러 |\n| E-seller Sherri | 쇼린이 | Landon | 랜던 | Sua | 수아 |\n| Echo | 에코 | Larry | 래리 | Suho | 수호 |\n| Edward | 에드워드 | Leo | 레오 | Suji | 수지 |\n| Elias | 엘리아스 | Liam | 리암 | Sujin | 수진 |\n| Elise | 엘리스 | Lindsay | 린지 | Sullock Hong | 홍설록 |\n| Ella | 엘라 | Liz | 리즈 | Sumin | 수민 |\n| Eman | 이만 | Lloyd | 로이드 | Sungbae | 성배 |\n| Emma | 엠마 | Logan | 로건 | Sunggyu | 성규 |\n| Eunbin | 은빈 | Lucille | 루실 | Sungho | 성호 |\n| Eunchae | 은채 | Luna | 루나 | Sunghoon | 성훈 |\n| Eunha | 은하 | Lydia | 리디아 | Sunghyun | 성현 |\n| Eunsol | 은솔 | Maddie | 매디 | Sungjun | 홈쇼핑 성준 |\n| Frankenstein | 프랑켄슈타인 | Maisie | 메이시 | Sungkwon | 성권 |\n| Furnando | 터르난도 | Margaret | 마가렛 | Sungtae | 성태 |\n| Gaeul | 가을 | Margot | 마고 | Sungwook | 성욱 |\n| Gahee | 가희 | Matthew | 매튜 | Sunyoung | 선영 |\n| George | 조지 | MBTI EF (F) | MBTI EF 여 | Suyoon | 수윤 |\n| GeumHee | 금희 | MBTI EF (M) | MBTI EF 남 | Sylvia | 실비아 |\n| Geunhyeok | 근혁 | MBTI ET (F) | MBTI ET 여 | Taebaek | 장태백 |\n| Geunseok | 근석 | MBTI ET (M) | MBTI ET 남 | Taeji | 태지 |\n| Geunwoo | 근우 | MBTI IF (F) | MBTI IF 여 | Taejoong | 태중 |\n| Geunyeong | 근영 | MBTI IF (M) | MBTI IF 남 | Taemin | 태민 |\n| Ggami | 까미 | MBTI IT (F) | MBTI IT 여 | Taesub | 태섭 |\n| Ggomi | 꼬미 | MBTI IT (M) | MBTI IT 남 | Taewoo | 태우 |\n| Glenda | 글렌다 | MC Kong | MC콩 | Teal | 틸 |\n| Goat Kim | 김고트 | MC TypeCast | MC 타캐 | Tessa | 테사 |\n| Gongchul | 공철 | Mia | 미아 | Tian | 티안 |\n| Gowoon | 고운 | Michael | 마이클 | Tim | 팀 |\n| Grace | 그레이스 | Mija | 오미자 | Tina | 티나 |\n| Graham | 그레이엄 | Mijin | 미진 | Toby | 토비 |\n| Gunseok | 건석 | Mikyung | 미경 | Tommy | 토미 |\n| Gunwoo | 건우 | Millie | 밀리 | Tyson | 타이슨 |\n| Guri | 구리 | Minchae | 민채 | UiChan | 의찬이 |\n| Gus | 거스 | Minji | 민지 | Uncle Hank | 행크 |\n| Ha Eun | 하은 | Minjoon | 민준 | Valerie | 밸러리 |\n| Haejun | 해준 | Minju | 민주 | Valkyrie | 발키리 |\n| Haerang | 해랑 | Minjung | 민정 | Vanessa | 바네사 |\n| Hailey | 헤일리 | Minsang | 민상 | Verna | 베르나 |\n| Hajun | 하준 | Minsu | 민수 | Victoria | 빅토리아 |\n| Hakchul | 학철 | Mio | 미오 | Viktor | 빅토르 |\n| Hamchu | 햄쮸 | Miran Choi | 최미란 | Vincent | 빈센트 |\n| Han Taesung Caster | 한태성 캐스터 | Mirine | 미리내 | Vivien | 비비안 |\n| Hana | 하나 | Miseon | 미선 | Wade | 웨이드 |\n| Hangyeol | 한결 | Miso | 미소 | Walter | 월터 |\n| Hanjun | 한준 | Mister Gop | 미스터 갑 | Wangkwon | 왕권 |\n| Hanna | 한나 | Monggun | 몽군 | Weather Reporter Sky | 하늘 캐스터 |\n| Hans | 한스 | Moonjung | 문정 | West | 웨스트 |\n| Hansol | 한솔 | Mooyeol | 무열 | Wonho | 원호 |\n| Hanyoung | 한영 | Morgan | 모건 | Wonkyung | 원경 |\n| Harin | 하린 | Moru | 머루 | Wonwoo | 원우 |\n| Harper | 하퍼 | Mrs. Claus | 미세스 클로스 | Wooju | 우주 |\n| Hayul | 하율 | Munseok | 문석 | Woosung | 우성 |\n| Heejoon | 희준 | Munsu | 문수 | Xavier | 자비에르 |\n| Helena | 헬레나 | Myeonghee | 명희 | Yeeun | 예은 |\n| Henry | 헨리 | Myungil | 명일 | Yejin | 예진 |\n| Homun Sim | 심호문 | Myungjoo | 명주 | Yejoon | 예준 |\n| Hoon | 훈 | Naeun | 나은 | YeLin | 예린 |\n| Hosik | 호식 | Najin | 나진 | Yena | 예나 |\n| Hosun | 호선 | Namjoon | 남준 | Yeonah | 연아 |\n| Hoyoung | 호영 | Nana | 나나 | Yeonggeol | 영걸 |\n| Hugh | 휴 | Nari | 개나리 | Yeonhwa | 연화 |\n| Hwimin | 휘민 | Nathan | 네이선 | Yeonja | 연자 |\n| Hyejung | 혜정 | Neel | 닐 | Yeonsuh | 연서 |\n| Hyelee | 혜리 | Neoguard | 네오가드 | Yeonwoo | 연우 |\n| Hyemin | 혜민 | News anchor Jinsub | 송진섭 앵커 | Yeseul | 예슬 |\n| Hyena | 혜나 | Newscaster John | 존 앵커 | Yongsik | 용식이 |\n| Hyeongjin | 형진 | Nia | 니아 | Yoonseo | 윤서 |\n| Hyera | 혜라 | Noa | 노아 | Yoonsung | 윤성 |\n| Hyesu | 홈쇼핑 혜수 | Noel | 노엘 | Younggil | 영길 |\n| Hyewon | 혜원 | Noeul | 노을 | Younghee | 영희 |\n| Hyun | 현 | Norah | 노라 | Younghwan | 영환 |\n| Hyunji | 현지 | Nova | 노바 | Youngji | 영지 |\n| Hyunjin | 현진 | Old radio | 레디오 | Youngkyu | 영규 |\n| Hyunju | 현주 | Oliver | 올리버 | Youngmok | 영목 |\n| Hyunkyung | 현경 | Olivia | 올리비아 | Yubin | 유빈 |\n| Hyunmin | 현민 | Oscar | 오스카 | Yujin | 유진 |\n| Hyunseung | 현승 | Owen | 오언 | Yumi | 유미 |\n| HyunWoo | 현우 | P-0150N | 포이즌 | Yumin | 유민 |\n| Ian | 이안 | Pacang | 파캉 | Yunbin | 윤빈 |\n| Icarus | 이카루스 | Paige | 페이지 | Yunjeong | 윤정 |\n| Igyeom | 이겸 | Pangpang | 팡팡 | Yura | 유라 |\n| Ijun | 이준 | Patricia | 패트리샤 | Yuri | 유리 |\n| Ilho | 일호 | Patrick | 패트릭 | Yuseong | 유성 |\n| Ina | 이나 | Peter | 피터 | Zoey | 조이 |\n| Inhwa | 인화 | Philip | 필립 | | |\n\n
\n" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "사람이 읽을 수 있는 보이스 이름" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입 — `original` 은 타입캐스트가 기본 제공하는 스톡 보이스, `custom` 은 사용자가 업로드/클론한 보이스입니다." } }, "description": "모델별로 그룹화된 감정과 향상된 메타데이터가 있는 V2 보이스 응답 모델" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대입니다." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별(`male` 또는 `female`)입니다." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "지원하는 TTS 모델과 감정 목록입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유 보이스 식별자입니다. 기본 보이스는 `tc_`, 커스텀 보이스는 `uc_`로 시작합니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "추천 사용 사례 목록입니다." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "`{\"eng\": \"Daejin\", \"kor\": \"대진\"}` 같은 다국어 보이스 이름입니다." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입(`original` 또는 `custom`)입니다." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "제공되는 경우 사용할 수 있는 미리듣기 오디오 URL입니다." } }, "description": "다국어 이름과 미리듣기 URL을 포함한 보이스 정보입니다." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "API 플랜.\n\n사용 가능한 값:\n- **free**: 제한된 크레딧의 무료 등급\n- **lite**: 중간 수준 크레딧의 라이트 플랜\n- **plus**: 높은 크레딧의 플러스 플랜\n- **custom**: 커스텀 엔터프라이즈 플랜" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "음성 합성에 사용할 TTS 모델 버전. 다양한 모델은 다양한 기능과 품질 수준을 제공합니다.\n\n사용 가능한 모델:\n- **ssfm-v30**: 향상된 플로우와 추가 감정 프리셋이 있는 최신 모델(권장)\n- **ssfm-v21**: 검증된 신뢰성과 일관된 품질을 갖춘 안정적인 모델\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS 모델 버전(예: ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "이 모델에서 지원되는 감정 목록" } }, "description": "버전 및 지원되는 감정을 포함한 모델 정보" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "영어 보이스 이름입니다." }, "kor": { "type": "string", "title": "Kor", "description": "한국어 보이스 이름입니다." } }, "description": "ISO 639-3 언어 코드를 키로 사용하는 다국어 보이스 이름입니다." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "보이스 타입 분류.\n\n- `original` — 타입캐스트가 기본 제공하는 스톡 보이스로, 모든 계정에서 사용 가능합니다.\n- `custom` — 사용자가 본인의 샘플을 업로드하거나 클로닝해서 만든 보이스입니다." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "성별 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **male**: 남성 보이스\n- **female**: 여성 보이스\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "음성 합성에 사용 가능한 감정 프리셋. 각 감정은 생성된 음성의 톤, 속도, 표현력에 영향을 줍니다.\n\n**ssfm-v21 지원 감정 (4종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n\n**ssfm-v30 지원 감정 (7종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n- whisper: 부드럽고 조용한 말\n- toneup: 더 높은 톤 강조\n- tonedown: 더 낮은 톤 강조\n\n/v2/voices API 응답을 통해 각 음성에 사용 가능한 감정을 확인하세요.\n" }, "SmartPrompt": { "type": "object", "title": "스마트 프롬프트 (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "TTSRequest의 `text` 필드 이후에 오는 텍스트. 감정 추론을 위한 전방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 전환을 예측하는 데 도움\r\n- 다음 컨텍스트가 없으면 비워 둡니다\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "프롬프트 유형을 식별하는 판별자 필드. 컨텍스트 인식 감정 추론을 위해 \"smart\"로 설정해야 합니다.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "TTSRequest의 `text` 필드 이전에 오는 텍스트. 감정 추론을 위한 후방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 빌드업과 컨텍스트를 이해하는 데 도움\r\n- 이전 컨텍스트가 없으면 비워 둡니다\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "프리셋 프롬프트 (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "프롬프트 유형을 식별하는 판별자 필드. 프리셋 기반 감정 제어를 위해 \"preset\"으로 설정해야 합니다.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "생성된 음성에 적용할 감정 프리셋.\r\n\r\n지원되는 감정: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "생성된 음성의 감정 표현 강도를 제어합니다.\r\n\r\n- 0.0: 완전히 중립적, 감정 색채 없음\r\n- 0.5: 미묘한 감정 힌트\r\n- 1.0: 표준 감정 표현(기본값)\r\n- 1.5: 강한 감정 강조\r\n- 2.0: 최대 강도, 매우 표현력 있음\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "콘텐츠 유형 필터링을 위한 보이스 사용 사례 카테고리. 각 보이스는 특정 콘텐츠 유형에 대한 적합성을 나타내는 하나 이상의 사용 사례로 태그됩니다.\n\n**사용 가능한 사용 사례:**\n- **Announcer**: 공공 발표 및 프레젠테이션\n- **Anime**: 애니메이션 보이스\n- **Audiobook**: 장문 내레이션 및 스토리텔링\n- **Conversational**: 챗봇 및 대화형 AI\n- **Documentary**: 다큐멘터리 내레이션 및 해설\n- **E-learning**: 교육 콘텐츠 및 튜토리얼\n- **Rapper**: 랩 및 음악 퍼포먼스\n- **Game**: 비디오 게임 보이스 및 내레이션\n- **Tiktok/Reels**: SNS 숏츠\n- **News**: 뉴스 방송\n- **Podcast**: 방송 및 팟캐스트 제작\n- **Voicemail**: IVR 시스템 및 음성 비서\n- **Ads**: 광고 및 홍보 콘텐츠\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "문제를 설명하는 오류 메시지" }, "message": { "type": "string", "description": "구조화된 오류에 대한 설명" }, "error_code": { "type": "string", "description": "구조화된 오류를 식별하는 코드" } }, "description": "기존 오류와 유효성 검사 오류는 `detail`을 사용하며, 구조화된 오류는 `error_code`와 `message`를 사용합니다." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "`status`가 `failed`일 때의 실패 사유입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "source": { "type": "string", "title": "Source", "description": "생성 방식(`instant` 또는 `professional`)입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "UTC 기준 생성 시각입니다." } }, "description": "인증된 계정이 소유한 커스텀 보이스입니다." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "추천 관련도 점수. 값이 높을수록 검색어와 더 잘 맞는 후보입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "`tc_` prefix 가 붙은 타입캐스트 보이스 식별자. 텍스트 음성 변환 요청의 `voice_id` 로 사용할 수 있습니다." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "사람이 읽을 수 있는 보이스 이름." } }, "description": "`GET /v1/voices/recommendations` 응답의 보이스 추천 후보. 관련도 순서로 정렬됩니다." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "커스텀 보이스의 현재 생성 또는 학습 상태입니다." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름 (1~30자)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "보이스가 클로닝된 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "`uc_` prefix 가 붙은 커스텀 보이스 식별자. `POST /v1/text-to-speech` 등 `voice_id` 를 받는 엔드포인트에 그대로 사용할 수 있습니다." } }, "description": "`POST /v1/voices/clone` 응답 — 퀵 클로닝으로 생성된 커스텀 보이스 메타데이터." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "세그먼트 구분값입니다. 항상 `pause`입니다." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "초 단위 쉼 길이입니다. 개별 쉼은 최대 10초, 모든 쉼의 합은 최대 60초입니다.", "exclusiveMinimum": 0 } }, "description": "크레딧 차감 없이 삽입되는 무음 구간입니다.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 단어 단위 정렬 구간." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "현재 구독 플랜명" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "사용 제한 정보" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "크레딧 사용 정보" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "구독 정보 응답 모델" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호/공백 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 문자 단위 정렬 구간." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." } }, "description": "퀵 클로닝 또는 프리미엄 클로닝 생성 요청 결과입니다." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "base64 로 인코딩된 오디오 바이트. `audio_format` 확장자로 디코딩해 파일로 저장할 수 있습니다." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "단어 단위 타임스탬프(문장부호 포함). 요청이 `granularity=char` 일 때는 `null`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "문자 단위 타임스탬프(문장부호와 공백 포함). 요청이 `granularity=word` 일 때는 `null`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "`audio` 필드의 오디오 인코딩 포맷 — `wav` 또는 `mp3` (요청의 `output.audio_format` 에 따라 결정)." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "생성된 오디오의 길이(초)." } }, "description": "TTS 생성 + 타임스탬프 정렬 통합 응답." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "오디오 샘플. WAV 또는 MP3, 최대 25MB, 5초 이상 150초 이하.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "보이스 이름 (1~30자)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "클로닝할 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." } }, "description": "퀵 클로닝 요청을 위한 multipart 본문." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "파일", "format": "binary", "description": "25 MiB 이하이면서 길이가 5초 이상 150초 이하인 WAV 또는 MP3 음성 파일 1개입니다.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "상품 내레이터", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." } }, "description": "퀵 클로닝용 multipart 요청 본문입니다." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "커스텀 보이스 이름", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "파일", "description": "학습에 사용할 WAV 또는 MP3 음성 파일입니다. 하나의 파일을 업로드할 수 있습니다.\r\n\r\n**업로드 제한**\r\n\r\n* WAV 또는 MP3 파일 1개\r\n* 파일 크기: 1 GiB 이하\r\n* 길이: 5분 이상 3시간 이하\r\n* 샘플레이트: 16 kHz 이상\r\n\r\n최상의 결과를 위해 다음 조건을 충족하는 오디오를 권장합니다.\r\n\r\n* 생성된 보이스가 재현하기를 원하는 말투와 최대한 가깝게 녹음해 주세요.\r\n* 배경 소음이 없는 조용한 환경에서 녹음해 주세요.\r\n* 한 명의 화자 목소리만 포함해 주세요.\r\n* `language` 필드에 지정한 언어로 녹음해 주세요.\r\n* 오디오 길이가 길수록 생성되는 보이스의 품질이 향상됩니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." }, "language": { "type": "string", "title": "Language", "example": "kor", "description": "`kor`, `eng` 같은 ISO 639-3 언어 코드입니다." } }, "description": "프리미엄 클로닝용 multipart 요청 본문입니다." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "인증을 위한 API 키. 타입캐스트 API 콘솔에서 API 키를 생성할 수 있습니다." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 보이스 목록 조회 > voice\_id가 포함된 모든 엔드포인트에서 사용할 수 있는 일반 보이스와 생성이 완료된 커스텀 보이스를 조회합니다. 커스텀 보이스가 먼저 표시되며 `voice_name`은 `eng`, `kor` 같은 ISO 639-3 언어 코드별 이름을 제공합니다. 모델, 성별, 연령대, 사용 사례, 보이스 유형 필터를 함께 사용할 수 있습니다. 미리듣기를 제공하지 않는 보이스와 커스텀 보이스의 `preview_url`은 `null`입니다. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "프로덕션 서버" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v3/voices": { "get": { "tags": [ "Voices" ], "x-mint": { "href": "/ko/api-reference/voices/list-voices" }, "summary": "보이스 목록 조회", "security": [ { "ApiKeyAuth": [] } ], "responses": { "200": { "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VoiceV3" }, "title": "Response Get Voices V3 V3 Voices Get" }, "example": [ { "age": null, "gender": null, "models": [ { "version": "ssfm-v30", "emotions": [ "normal" ] } ], "voice_id": "uc_6700000000000000000000aa", "use_cases": [], "voice_name": { "eng": "My Voice", "kor": "내 보이스" }, "voice_type": "custom", "preview_url": null }, { "age": "young_adult", "gender": "female", "models": [ { "version": "ssfm-v30", "emotions": [ "normal", "happy", "sad", "angry" ] } ], "voice_id": "tc_6045d56d5f9ae03ac175cf73", "use_cases": [ "Game", "Anime" ], "voice_name": { "eng": "Valkyrie", "kor": "발키리" }, "voice_type": "original", "preview_url": "https://static2.typecast.ai/data/actor/valkyrie.mp3" } ] } }, "description": "보이스 목록을 조회했습니다." }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "인증에 실패했습니다." }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid query parameter" } } }, "description": "유효하지 않은 쿼리 파라미터입니다." } }, "parameters": [ { "in": "query", "name": "model", "schema": { "$ref": "#/components/schemas/TTSModel", "description": "특정 모델로 필터링 (예: ssfm-v21, ssfm-v30, ssfm-v31)" }, "required": false, "description": "`ssfm-v21`, `ssfm-v30`, `ssfm-v31` 같은 TTS 모델로 필터링합니다." }, { "in": "query", "name": "gender", "schema": { "$ref": "#/components/schemas/GenderEnum", "description": "성별 필터 (male/female)" }, "required": false, "description": "보이스 성별(`male` 또는 `female`)로 필터링합니다." }, { "in": "query", "name": "age", "schema": { "$ref": "#/components/schemas/AgeEnum", "description": "나이대 필터 (child/teenager/young_adult/middle_age/elder)" }, "required": false, "description": "연령대(`child`, `teenager`, `young_adult`, `middle_age`, `elder`)로 필터링합니다." }, { "in": "query", "name": "use_cases", "schema": { "type": "string", "title": "Use Cases", "description": "사용 사례 필터 (키워드 포함 매칭, 예: Ads)" }, "required": false, "description": "`Ads` 같은 사용 사례 키워드로 필터링합니다." }, { "in": "query", "name": "voice_type", "schema": { "$ref": "#/components/schemas/VoiceType", "description": "Voice 타입 필터 (original / custom)" }, "required": false, "description": "보이스 타입(`original` 또는 `custom`)으로 필터링합니다." } ], "description": "voice\\_id가 포함된 모든 엔드포인트에서 사용할 수 있는 일반 보이스와 생성이 완료된 커스텀 보이스를 조회합니다. 커스텀 보이스가 먼저 표시되며 `voice_name`은 `eng`, `kor` 같은 ISO 639-3 언어 코드별 이름을 제공합니다.\r\n\r\n모델, 성별, 연령대, 사용 사례, 보이스 유형 필터를 함께 사용할 수 있습니다. 미리듣기를 제공하지 않는 보이스와 커스텀 보이스의 `preview_url`은 `null`입니다.", "operationId": "get_voices_v3_v3_voices_get", "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request GET \\\n --url 'https://api.typecast.ai/v3/voices?model=ssfm-v30&voice_type=original' \\\n --header 'X-API-KEY: '\n" }, { "lang": "Python", "label": "Python (requests)", "source": "import requests\n\nresponse = requests.get(\n \"https://api.typecast.ai/v3/voices\",\n headers={\"X-API-KEY\": \"\"},\n params={\"model\": \"ssfm-v30\", \"voice_type\": \"original\"},\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "허용되는 최대 동시 요청 수" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "전체 커스텀 보이스 슬롯 한도 (professional 포함)" }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "사용 제한 정보" }, "Prompt": { "title": "프롬프트 (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "적용할 감정 프리셋.\r\n\r\nssfm-v21 지원 감정: normal, happy, sad, angry\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "example": 1, "description": "감정 표현 강도 제어(0.0~2.0).\r\n\r\n- 0.0: 완전히 중립적\r\n- 1.0: 표준 표현(기본값)\r\n- 2.0: 최대 강도\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "연령대 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **child**: 어린이 보이스(12세 미만)\n- **teenager**: 청소년 보이스(13-19세)\n- **young_adult**: 청년 보이스(20-35세)\n- **middle_age**: 중년 보이스(36-60세)\n- **elder**: 노년 보이스(60세 이상)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "플랜에서 기본으로 제공하는 총 크레딧" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "사용된 크레딧 수" } }, "description": "크레딧 사용 정보" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대 분류(어린이/청소년/청년/중년/노년)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별 분류(남성/여성)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "사용 가능한 감정이 있는 지원되는 TTS 모델 목록(예: [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유한 보이스 식별자. 기본 제공 보이스는 `tc_` prefix (예: `tc_60e5426de8b95f1d3000d7b5`), `POST /v1/voices/clone` 으로 만든 커스텀 보이스는 `uc_` prefix 를 사용합니다. 본인이 소유한 `uc_` 보이스도 `/v2/voices` 응답에 포함됩니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "이 보이스가 적합한 사용 사례 카테고리 목록\n\n## 보이스 이름 참고\n\n`voice_name` 필드는 영문으로만 반환됩니다. 아래 목록에서 한국어 표기를 확인하실 수 있습니다.\n\n
\n보이스 이름 목록 펼치기 (542개)\n\n| 영어 | 한국어 | 영어 | 한국어 | 영어 | 한국어 |\n| --- | --- | --- | --- | --- | --- |\n| Aaron | 에런 | Inhye | 인혜 | Piljae | 필재 |\n| Abigail | 아비게일 | Inseong | 인성 | Pyeonghwa | 평화 |\n| Ae-ran | 애란 | Instructor Han | 한유격 교관 | Rachel | 레이첼 |\n| Ael | 아엘 | Insun | 인선 | Ravi | 라비 |\n| Agatha | 아가사 | Jabbaba | 자바바 | Rayeon | 라연 |\n| Aiden | 에이든 | Jack | 잭 | Rebecca | 레베카 |\n| Alena | 알레나 | Jack-o’-Lantern | 잭오랜턴 | Reporter Bona | 다보나 기자 |\n| Alex | 알렉스 | Jackson | 잭슨 | Reporter Catalina | 카탈리나 기자 |\n| Amber | 엠버 | Jaeho | 재호 | Reporter Jinsub | 송진섭 기자 |\n| Anchor Hwa | 김경화 앵커 | Jaehun | 재훈 | Reporter Kang | 강수정 기자 |\n| Angela | 안젤라 | Jaejun | 재준 | Reporter Lee | 이승주 기자 |\n| Anja | 안냐 | Jaekyung | 재경 | Reporter Parkchan | 박찬 기자 |\n| Annabelle the Ghost | 귀신 애나벨 | Jaesun | 재선 | Rex | 렉스 |\n| Annie | 애니 | JaeYi | 재이 | Riley | 라일리 |\n| April | 에이프릴 | Jaeyoung | 재영 | Risan Ji | 지리산 |\n| Arang | 아랑 | Jain | 자인 | Rita | 리타 |\n| Ari | 아리 | Jake | 제이크 | River | 리버 |\n| Arin | 아린 | Janet | 재닛 | Robert | 로버트 |\n| Athena | 아테나 | Jangho | 장호 | Robo | 로보 |\n| Audrey | 오드리 | Jangwoon | 장운 | Romi | 로미 |\n| Avery | 에이버리 | Jechan | 제찬 | Ron | 론 |\n| Avong | 아봉 | Jeff | 제프 | Roro | 로로 |\n| Azzi | 아찌 | Jenna | 제나 | Rowoon | 로운 |\n| Babilon | 바빌론 | Jennifer | 제니퍼 | Royce | 로이스 |\n| Bell | 벨 | Jeong Choi | 최정 | Rudolph | 루돌프 |\n| Ben | 벤 | Jeongah | 정아 | Ruri | 루리 |\n| Benny | 베니 | JeongJin | 정진 | Rusty | 러스티 |\n| Beri | 베리 | Jeongseob | 정섭 | Ryan | 라이언 |\n| Billie | 빌리 | Jerome | 제롬 | Ryueun | 류은 |\n| Bomi | 이보미 | Jewby | 쥬비 | Sabrina the Witch | 마녀 사브리나 |\n| Bonggyu | 봉규 | Jian | 지안 | Saeron | 새론 |\n| Bongman Kim | 김봉만 | Jicheol | 지철 | Salty Chan-gu | 삐뚤어진 찬구 |\n| Bono | 보노 | Jihee | 지희 | Samantha | 사만다 |\n| Bora | 보라 | Jihoon | 지훈 | Sammy | 새미 |\n| Brad | 브래드 | Jihyun | 지현 | Sangdo | 상도 |\n| Bruce | 브루스 | Jimmy | 지미 | Sanghoon | 상훈 |\n| Buddy | 버디 | Jin | 진 | Sangwoo | 상우 |\n| Bumsoo | 범수 | Jinhan | 진한 | Santa | 산타 |\n| Buttaguy | 빠다가이 | Jinhang Kim | 김진행 | Santa Claus | 산타클로스 |\n| Caitlyn | 케이틀린 | Jinhee | 진희 | Santa Reporter | VJ 싼타 |\n| Callan | 칼란 | Jinhyuk | 진혁 | Sara | 세라 |\n| Camila | 카밀라 | Jinseo | 진서 | Sean | 션 |\n| Captain Bill | 캡틴 빌 | Jinung | 진웅 | SeHee | 세희 |\n| Carl | 칼 | Jinwoo | 진우 | Seheon | 세헌 |\n| Carlos | 카를로스 | Jiwoo | 지우 | Sejin | 세진 |\n| Carol | 캐롤 | Jiyoon | 지윤 | Sena | 세나 |\n| Catherine | 캐서린 | Jiyoung | 지영 | Seohee | 서희 |\n| Chad | 채드 | Jodie | 조디 | Seojin | 서진 |\n| Chaeah | 채아 | Jolly | 졸리 | Seojoon | 서준 |\n| Chan-gu | 찬구 | Jongdae | 종대 | Seok Choi | 최석 |\n| Changbae | 창배 | Jooeun | 주은 | SeokPil | 한석필 |\n| Changhee | 창희 | Joonghyun | 중현 | Seokpyo | 석표 |\n| Changmin | 창민 | Joongsik | 중식 | Seolhwa | 설화 |\n| Changsu | 박창수 | Joonkyu | 준규 | Seonha | 선하 |\n| Chanhyuk | 찬혁 | Joshua | 조슈아 | Seoyeon | 서연 |\n| Charlotte | 샬롯 | Juha | 주하 | Seoyoon | 서윤 |\n| Chase | 체이스 | Juho | 주호 | Seungah | 승아 |\n| Cheolhoon | 철훈 | Jungbong | 정봉 | Seungheon | 승헌 |\n| Cheolyong | 리철용 | Junghee | 정희 | Seungho | 승호 |\n| Cherry | 체리 | Jungjae | 정재 | Seunghwa | 승화 |\n| Chester | 체스터 | Jungmin | 정민 | Seungjae | 승재 |\n| Chiho | 치호 | Jungseok | 정석 | Seungmoon | 승문 |\n| Chloe | 클로이 | Jungsoon | 정순 | Seungwon | 승원 |\n| Choyeon | 강초연 교관 | Jungwon | 정원 | Seungyeon | 승연 |\n| Chungah | 청아 | Junhee | 준희 | Sewoo | 세우 |\n| Chunsik Kang | 강춘식 기자 | Junho | 준호 | Shana | 샤나 |\n| Claire | 클레어 | JunKi | 준기 | Sheriff Kim | 김반장 |\n| Classic Narrator | 미스타 변사 | Junsang | 준상 | Shinhe | 신혜 |\n| Cole | 콜 | Junseong | 준성 | Shinwook | 신욱 |\n| Cox | 콕스 | Justice Roh | 정의로 | Shotgun | 샷건 |\n| Cyrus | 사이러스 | Justin | 저스틴 | Simon | 사이먼 |\n| Dabin | 다빈 | Juwon | 주원 | Sindarin | 신다린 |\n| Daegil | 대길 | Juyoung | 주영 | Sio | 시오 |\n| Dahee | 다희 | K-Santa | 산타 할아버지 | Siwon | 시원 |\n| Dahyeon | 다현 | Kangil | 강일 | Siwoo | 시우 |\n| Damian | 데이미언 | Kanno | 칸노 | Siyeon | 시연 |\n| Dan | 댄 | Katie | 케이티 | Skylar | 스카일러 |\n| Dana | 다나 | Kelly | 켈리 | Slushy | 슬러시 |\n| Dasom | 다솜 | Kelsey | 켈시 | Smoke | 스모크 |\n| David | 데이비드 | Kevin | 케빈 | Sohye | 소혜 |\n| Dean | 딘 | Keybo | 키보 | Sojin | 소진 |\n| Deokhwan | 덕환 | Kijang Kim | 김기장 | Soobin | 수빈 |\n| Dohan | 도한 | Killian the Vampire | 뱀파이어 킬리언 | Sookhee | 숙희 |\n| Dohee | 도희 | Kiseob | 기섭 | Sooni | 순이 |\n| Dohyun | 도현 | Klip Kim | 클립 킴 | Sophia | 소피아 |\n| Dollar Jr. | 달러 주니어 | Koombo | 쿰보 | Sora | 소라 |\n| Doug | 더그 | Kristen | 크리스틴 | Soye | 소예 |\n| Doughnut | 도넛 | Kukhee | 국희 | Soyi | 소이 |\n| DU5T | 더스트 | Kwonil | 권일 | Soyoung | 소영 |\n| Duckchun | 덕춘 | Kyumin | 규민 | Soyul | 소율 |\n| Duke | 듀크 | Kyungho | 경호 | Spice | 스파이스 |\n| Dukgu | 덕구 | Kyungsoo | 경수 | Sportscaster Kang | 강수정 캐스터 |\n| Duman | 두만 | Kyungsook | 경숙 | Sportscaster Tony | 이영광 캐스터 |\n| Dupil | 곽두필 | Lady Cho | 단희빈 | Starling | 스탈링 |\n| DVZY | 데이지 | Lala | 라라 | Stephanie | 스테파니 |\n| Dylan | 딜런 | Lamie | 라미 | Storyteller Jinsub | 송진섭 스토리텔러 |\n| E-seller Sherri | 쇼린이 | Landon | 랜던 | Sua | 수아 |\n| Echo | 에코 | Larry | 래리 | Suho | 수호 |\n| Edward | 에드워드 | Leo | 레오 | Suji | 수지 |\n| Elias | 엘리아스 | Liam | 리암 | Sujin | 수진 |\n| Elise | 엘리스 | Lindsay | 린지 | Sullock Hong | 홍설록 |\n| Ella | 엘라 | Liz | 리즈 | Sumin | 수민 |\n| Eman | 이만 | Lloyd | 로이드 | Sungbae | 성배 |\n| Emma | 엠마 | Logan | 로건 | Sunggyu | 성규 |\n| Eunbin | 은빈 | Lucille | 루실 | Sungho | 성호 |\n| Eunchae | 은채 | Luna | 루나 | Sunghoon | 성훈 |\n| Eunha | 은하 | Lydia | 리디아 | Sunghyun | 성현 |\n| Eunsol | 은솔 | Maddie | 매디 | Sungjun | 홈쇼핑 성준 |\n| Frankenstein | 프랑켄슈타인 | Maisie | 메이시 | Sungkwon | 성권 |\n| Furnando | 터르난도 | Margaret | 마가렛 | Sungtae | 성태 |\n| Gaeul | 가을 | Margot | 마고 | Sungwook | 성욱 |\n| Gahee | 가희 | Matthew | 매튜 | Sunyoung | 선영 |\n| George | 조지 | MBTI EF (F) | MBTI EF 여 | Suyoon | 수윤 |\n| GeumHee | 금희 | MBTI EF (M) | MBTI EF 남 | Sylvia | 실비아 |\n| Geunhyeok | 근혁 | MBTI ET (F) | MBTI ET 여 | Taebaek | 장태백 |\n| Geunseok | 근석 | MBTI ET (M) | MBTI ET 남 | Taeji | 태지 |\n| Geunwoo | 근우 | MBTI IF (F) | MBTI IF 여 | Taejoong | 태중 |\n| Geunyeong | 근영 | MBTI IF (M) | MBTI IF 남 | Taemin | 태민 |\n| Ggami | 까미 | MBTI IT (F) | MBTI IT 여 | Taesub | 태섭 |\n| Ggomi | 꼬미 | MBTI IT (M) | MBTI IT 남 | Taewoo | 태우 |\n| Glenda | 글렌다 | MC Kong | MC콩 | Teal | 틸 |\n| Goat Kim | 김고트 | MC TypeCast | MC 타캐 | Tessa | 테사 |\n| Gongchul | 공철 | Mia | 미아 | Tian | 티안 |\n| Gowoon | 고운 | Michael | 마이클 | Tim | 팀 |\n| Grace | 그레이스 | Mija | 오미자 | Tina | 티나 |\n| Graham | 그레이엄 | Mijin | 미진 | Toby | 토비 |\n| Gunseok | 건석 | Mikyung | 미경 | Tommy | 토미 |\n| Gunwoo | 건우 | Millie | 밀리 | Tyson | 타이슨 |\n| Guri | 구리 | Minchae | 민채 | UiChan | 의찬이 |\n| Gus | 거스 | Minji | 민지 | Uncle Hank | 행크 |\n| Ha Eun | 하은 | Minjoon | 민준 | Valerie | 밸러리 |\n| Haejun | 해준 | Minju | 민주 | Valkyrie | 발키리 |\n| Haerang | 해랑 | Minjung | 민정 | Vanessa | 바네사 |\n| Hailey | 헤일리 | Minsang | 민상 | Verna | 베르나 |\n| Hajun | 하준 | Minsu | 민수 | Victoria | 빅토리아 |\n| Hakchul | 학철 | Mio | 미오 | Viktor | 빅토르 |\n| Hamchu | 햄쮸 | Miran Choi | 최미란 | Vincent | 빈센트 |\n| Han Taesung Caster | 한태성 캐스터 | Mirine | 미리내 | Vivien | 비비안 |\n| Hana | 하나 | Miseon | 미선 | Wade | 웨이드 |\n| Hangyeol | 한결 | Miso | 미소 | Walter | 월터 |\n| Hanjun | 한준 | Mister Gop | 미스터 갑 | Wangkwon | 왕권 |\n| Hanna | 한나 | Monggun | 몽군 | Weather Reporter Sky | 하늘 캐스터 |\n| Hans | 한스 | Moonjung | 문정 | West | 웨스트 |\n| Hansol | 한솔 | Mooyeol | 무열 | Wonho | 원호 |\n| Hanyoung | 한영 | Morgan | 모건 | Wonkyung | 원경 |\n| Harin | 하린 | Moru | 머루 | Wonwoo | 원우 |\n| Harper | 하퍼 | Mrs. Claus | 미세스 클로스 | Wooju | 우주 |\n| Hayul | 하율 | Munseok | 문석 | Woosung | 우성 |\n| Heejoon | 희준 | Munsu | 문수 | Xavier | 자비에르 |\n| Helena | 헬레나 | Myeonghee | 명희 | Yeeun | 예은 |\n| Henry | 헨리 | Myungil | 명일 | Yejin | 예진 |\n| Homun Sim | 심호문 | Myungjoo | 명주 | Yejoon | 예준 |\n| Hoon | 훈 | Naeun | 나은 | YeLin | 예린 |\n| Hosik | 호식 | Najin | 나진 | Yena | 예나 |\n| Hosun | 호선 | Namjoon | 남준 | Yeonah | 연아 |\n| Hoyoung | 호영 | Nana | 나나 | Yeonggeol | 영걸 |\n| Hugh | 휴 | Nari | 개나리 | Yeonhwa | 연화 |\n| Hwimin | 휘민 | Nathan | 네이선 | Yeonja | 연자 |\n| Hyejung | 혜정 | Neel | 닐 | Yeonsuh | 연서 |\n| Hyelee | 혜리 | Neoguard | 네오가드 | Yeonwoo | 연우 |\n| Hyemin | 혜민 | News anchor Jinsub | 송진섭 앵커 | Yeseul | 예슬 |\n| Hyena | 혜나 | Newscaster John | 존 앵커 | Yongsik | 용식이 |\n| Hyeongjin | 형진 | Nia | 니아 | Yoonseo | 윤서 |\n| Hyera | 혜라 | Noa | 노아 | Yoonsung | 윤성 |\n| Hyesu | 홈쇼핑 혜수 | Noel | 노엘 | Younggil | 영길 |\n| Hyewon | 혜원 | Noeul | 노을 | Younghee | 영희 |\n| Hyun | 현 | Norah | 노라 | Younghwan | 영환 |\n| Hyunji | 현지 | Nova | 노바 | Youngji | 영지 |\n| Hyunjin | 현진 | Old radio | 레디오 | Youngkyu | 영규 |\n| Hyunju | 현주 | Oliver | 올리버 | Youngmok | 영목 |\n| Hyunkyung | 현경 | Olivia | 올리비아 | Yubin | 유빈 |\n| Hyunmin | 현민 | Oscar | 오스카 | Yujin | 유진 |\n| Hyunseung | 현승 | Owen | 오언 | Yumi | 유미 |\n| HyunWoo | 현우 | P-0150N | 포이즌 | Yumin | 유민 |\n| Ian | 이안 | Pacang | 파캉 | Yunbin | 윤빈 |\n| Icarus | 이카루스 | Paige | 페이지 | Yunjeong | 윤정 |\n| Igyeom | 이겸 | Pangpang | 팡팡 | Yura | 유라 |\n| Ijun | 이준 | Patricia | 패트리샤 | Yuri | 유리 |\n| Ilho | 일호 | Patrick | 패트릭 | Yuseong | 유성 |\n| Ina | 이나 | Peter | 피터 | Zoey | 조이 |\n| Inhwa | 인화 | Philip | 필립 | | |\n\n
\n" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "사람이 읽을 수 있는 보이스 이름" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입 — `original` 은 타입캐스트가 기본 제공하는 스톡 보이스, `custom` 은 사용자가 업로드/클론한 보이스입니다." } }, "description": "모델별로 그룹화된 감정과 향상된 메타데이터가 있는 V2 보이스 응답 모델" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대입니다." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별(`male` 또는 `female`)입니다." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "지원하는 TTS 모델과 감정 목록입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유 보이스 식별자입니다. 기본 보이스는 `tc_`, 커스텀 보이스는 `uc_`로 시작합니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "추천 사용 사례 목록입니다." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "`{\"eng\": \"Daejin\", \"kor\": \"대진\"}` 같은 다국어 보이스 이름입니다." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입(`original` 또는 `custom`)입니다." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "제공되는 경우 사용할 수 있는 미리듣기 오디오 URL입니다." } }, "description": "다국어 이름과 미리듣기 URL을 포함한 보이스 정보입니다." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "API 플랜.\n\n사용 가능한 값:\n- **free**: 제한된 크레딧의 무료 등급\n- **lite**: 중간 수준 크레딧의 라이트 플랜\n- **plus**: 높은 크레딧의 플러스 플랜\n- **custom**: 커스텀 엔터프라이즈 플랜" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "음성 합성에 사용할 TTS 모델 버전. 다양한 모델은 다양한 기능과 품질 수준을 제공합니다.\n\n사용 가능한 모델:\n- **ssfm-v30**: 향상된 플로우와 추가 감정 프리셋이 있는 최신 모델(권장)\n- **ssfm-v21**: 검증된 신뢰성과 일관된 품질을 갖춘 안정적인 모델\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS 모델 버전(예: ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "이 모델에서 지원되는 감정 목록" } }, "description": "버전 및 지원되는 감정을 포함한 모델 정보" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "영어 보이스 이름입니다." }, "kor": { "type": "string", "title": "Kor", "description": "한국어 보이스 이름입니다." } }, "description": "ISO 639-3 언어 코드를 키로 사용하는 다국어 보이스 이름입니다." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "보이스 타입 분류.\n\n- `original` — 타입캐스트가 기본 제공하는 스톡 보이스로, 모든 계정에서 사용 가능합니다.\n- `custom` — 사용자가 본인의 샘플을 업로드하거나 클로닝해서 만든 보이스입니다." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "성별 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **male**: 남성 보이스\n- **female**: 여성 보이스\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "음성 합성에 사용 가능한 감정 프리셋. 각 감정은 생성된 음성의 톤, 속도, 표현력에 영향을 줍니다.\n\n**ssfm-v21 지원 감정 (4종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n\n**ssfm-v30 지원 감정 (7종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n- whisper: 부드럽고 조용한 말\n- toneup: 더 높은 톤 강조\n- tonedown: 더 낮은 톤 강조\n\n/v2/voices API 응답을 통해 각 음성에 사용 가능한 감정을 확인하세요.\n" }, "SmartPrompt": { "type": "object", "title": "스마트 프롬프트 (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "TTSRequest의 `text` 필드 이후에 오는 텍스트. 감정 추론을 위한 전방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 전환을 예측하는 데 도움\r\n- 다음 컨텍스트가 없으면 비워 둡니다\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "프롬프트 유형을 식별하는 판별자 필드. 컨텍스트 인식 감정 추론을 위해 \"smart\"로 설정해야 합니다.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "TTSRequest의 `text` 필드 이전에 오는 텍스트. 감정 추론을 위한 후방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 빌드업과 컨텍스트를 이해하는 데 도움\r\n- 이전 컨텍스트가 없으면 비워 둡니다\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "프리셋 프롬프트 (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "프롬프트 유형을 식별하는 판별자 필드. 프리셋 기반 감정 제어를 위해 \"preset\"으로 설정해야 합니다.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "생성된 음성에 적용할 감정 프리셋.\r\n\r\n지원되는 감정: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "생성된 음성의 감정 표현 강도를 제어합니다.\r\n\r\n- 0.0: 완전히 중립적, 감정 색채 없음\r\n- 0.5: 미묘한 감정 힌트\r\n- 1.0: 표준 감정 표현(기본값)\r\n- 1.5: 강한 감정 강조\r\n- 2.0: 최대 강도, 매우 표현력 있음\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "콘텐츠 유형 필터링을 위한 보이스 사용 사례 카테고리. 각 보이스는 특정 콘텐츠 유형에 대한 적합성을 나타내는 하나 이상의 사용 사례로 태그됩니다.\n\n**사용 가능한 사용 사례:**\n- **Announcer**: 공공 발표 및 프레젠테이션\n- **Anime**: 애니메이션 보이스\n- **Audiobook**: 장문 내레이션 및 스토리텔링\n- **Conversational**: 챗봇 및 대화형 AI\n- **Documentary**: 다큐멘터리 내레이션 및 해설\n- **E-learning**: 교육 콘텐츠 및 튜토리얼\n- **Rapper**: 랩 및 음악 퍼포먼스\n- **Game**: 비디오 게임 보이스 및 내레이션\n- **Tiktok/Reels**: SNS 숏츠\n- **News**: 뉴스 방송\n- **Podcast**: 방송 및 팟캐스트 제작\n- **Voicemail**: IVR 시스템 및 음성 비서\n- **Ads**: 광고 및 홍보 콘텐츠\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "문제를 설명하는 오류 메시지" }, "message": { "type": "string", "description": "구조화된 오류에 대한 설명" }, "error_code": { "type": "string", "description": "구조화된 오류를 식별하는 코드" } }, "description": "기존 오류와 유효성 검사 오류는 `detail`을 사용하며, 구조화된 오류는 `error_code`와 `message`를 사용합니다." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "`status`가 `failed`일 때의 실패 사유입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "source": { "type": "string", "title": "Source", "description": "생성 방식(`instant` 또는 `professional`)입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "UTC 기준 생성 시각입니다." } }, "description": "인증된 계정이 소유한 커스텀 보이스입니다." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "추천 관련도 점수. 값이 높을수록 검색어와 더 잘 맞는 후보입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "`tc_` prefix 가 붙은 타입캐스트 보이스 식별자. 텍스트 음성 변환 요청의 `voice_id` 로 사용할 수 있습니다." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "사람이 읽을 수 있는 보이스 이름." } }, "description": "`GET /v1/voices/recommendations` 응답의 보이스 추천 후보. 관련도 순서로 정렬됩니다." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "커스텀 보이스의 현재 생성 또는 학습 상태입니다." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름 (1~30자)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "보이스가 클로닝된 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "`uc_` prefix 가 붙은 커스텀 보이스 식별자. `POST /v1/text-to-speech` 등 `voice_id` 를 받는 엔드포인트에 그대로 사용할 수 있습니다." } }, "description": "`POST /v1/voices/clone` 응답 — 퀵 클로닝으로 생성된 커스텀 보이스 메타데이터." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "세그먼트 구분값입니다. 항상 `pause`입니다." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "초 단위 쉼 길이입니다. 개별 쉼은 최대 10초, 모든 쉼의 합은 최대 60초입니다.", "exclusiveMinimum": 0 } }, "description": "크레딧 차감 없이 삽입되는 무음 구간입니다.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 단어 단위 정렬 구간." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "현재 구독 플랜명" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "사용 제한 정보" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "크레딧 사용 정보" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "구독 정보 응답 모델" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호/공백 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 문자 단위 정렬 구간." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." } }, "description": "퀵 클로닝 또는 프리미엄 클로닝 생성 요청 결과입니다." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "base64 로 인코딩된 오디오 바이트. `audio_format` 확장자로 디코딩해 파일로 저장할 수 있습니다." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "단어 단위 타임스탬프(문장부호 포함). 요청이 `granularity=char` 일 때는 `null`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "문자 단위 타임스탬프(문장부호와 공백 포함). 요청이 `granularity=word` 일 때는 `null`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "`audio` 필드의 오디오 인코딩 포맷 — `wav` 또는 `mp3` (요청의 `output.audio_format` 에 따라 결정)." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "생성된 오디오의 길이(초)." } }, "description": "TTS 생성 + 타임스탬프 정렬 통합 응답." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "오디오 샘플. WAV 또는 MP3, 최대 25MB, 5초 이상 150초 이하.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "보이스 이름 (1~30자)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "클로닝할 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." } }, "description": "퀵 클로닝 요청을 위한 multipart 본문." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "파일", "format": "binary", "description": "25 MiB 이하이면서 길이가 5초 이상 150초 이하인 WAV 또는 MP3 음성 파일 1개입니다.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "상품 내레이터", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." } }, "description": "퀵 클로닝용 multipart 요청 본문입니다." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "커스텀 보이스 이름", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "파일", "description": "학습에 사용할 WAV 또는 MP3 음성 파일입니다. 하나의 파일을 업로드할 수 있습니다.\r\n\r\n**업로드 제한**\r\n\r\n* WAV 또는 MP3 파일 1개\r\n* 파일 크기: 1 GiB 이하\r\n* 길이: 5분 이상 3시간 이하\r\n* 샘플레이트: 16 kHz 이상\r\n\r\n최상의 결과를 위해 다음 조건을 충족하는 오디오를 권장합니다.\r\n\r\n* 생성된 보이스가 재현하기를 원하는 말투와 최대한 가깝게 녹음해 주세요.\r\n* 배경 소음이 없는 조용한 환경에서 녹음해 주세요.\r\n* 한 명의 화자 목소리만 포함해 주세요.\r\n* `language` 필드에 지정한 언어로 녹음해 주세요.\r\n* 오디오 길이가 길수록 생성되는 보이스의 품질이 향상됩니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." }, "language": { "type": "string", "title": "Language", "example": "kor", "description": "`kor`, `eng` 같은 ISO 639-3 언어 코드입니다." } }, "description": "프리미엄 클로닝용 multipart 요청 본문입니다." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "인증을 위한 API 키. 타입캐스트 API 콘솔에서 API 키를 생성할 수 있습니다." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 보이스 상세 정보 조회 > 계정에서 사용할 수 있는 기본 보이스 또는 생성이 완료된 커스텀 보이스 한 개를 조회합니다. 다국어 이름, 지원 모델과 감정, 추천 사용 사례, 제공 가능한 경우 미리듣기 URL을 반환합니다. 기본 보이스 ID는 `tc_`, 커스텀 보이스 ID는 `uc_`로 시작합니다. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "프로덕션 서버" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v3/voices/{voice_id}": { "get": { "tags": [ "Voices" ], "x-mint": { "href": "/ko/api-reference/voices/get-voice-details" }, "summary": "보이스 상세 정보 조회", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VoiceV3" }, "example": { "age": "young_adult", "gender": "female", "models": [ { "version": "ssfm-v30", "emotions": [ "normal", "happy", "sad", "angry" ] } ], "voice_id": "tc_6045d56d5f9ae03ac175cf73", "use_cases": [ "Game", "Anime" ], "voice_name": { "eng": "Valkyrie", "kor": "발키리" }, "voice_type": "original", "preview_url": "https://static2.typecast.ai/data/actor/valkyrie.mp3" } } }, "description": "보이스 상세 정보를 조회했습니다." }, "400": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Invalid voice ID", "error_code": "INVALID_VOICE_ID" } } }, "description": "보이스 ID 형식이 올바르지 않습니다." }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "인증에 실패했습니다." }, "404": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Voice not found", "error_code": "VOICE_NOT_FOUND" } } }, "description": "보이스가 없거나 이 계정에서 사용할 수 없습니다." }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid request format" } } }, "description": "유효하지 않은 보이스 ID입니다." } }, "parameters": [ { "in": "path", "name": "voice_id", "schema": { "type": "string", "title": "Voice Id" }, "required": true, "description": "조회할 보이스의 고유 식별자입니다." } ], "description": "계정에서 사용할 수 있는 기본 보이스 또는 생성이 완료된 커스텀 보이스 한 개를 조회합니다. 다국어 이름, 지원 모델과 감정, 추천 사용 사례, 제공 가능한 경우 미리듣기 URL을 반환합니다. 기본 보이스 ID는 `tc_`, 커스텀 보이스 ID는 `uc_`로 시작합니다.", "operationId": "get_voice_v3_v3_voices__voice_id__get", "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request GET \\\n --url https://api.typecast.ai/v3/voices/tc_6045d56d5f9ae03ac175cf73 \\\n --header 'X-API-KEY: '\n" }, { "lang": "Python", "label": "Python (requests)", "source": "import requests\n\nresponse = requests.get(\n \"https://api.typecast.ai/v3/voices/tc_6045d56d5f9ae03ac175cf73\",\n headers={\"X-API-KEY\": \"\"},\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "허용되는 최대 동시 요청 수" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "전체 커스텀 보이스 슬롯 한도 (professional 포함)" }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "사용 제한 정보" }, "Prompt": { "title": "프롬프트 (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "적용할 감정 프리셋.\r\n\r\nssfm-v21 지원 감정: normal, happy, sad, angry\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "example": 1, "description": "감정 표현 강도 제어(0.0~2.0).\r\n\r\n- 0.0: 완전히 중립적\r\n- 1.0: 표준 표현(기본값)\r\n- 2.0: 최대 강도\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "연령대 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **child**: 어린이 보이스(12세 미만)\n- **teenager**: 청소년 보이스(13-19세)\n- **young_adult**: 청년 보이스(20-35세)\n- **middle_age**: 중년 보이스(36-60세)\n- **elder**: 노년 보이스(60세 이상)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "플랜에서 기본으로 제공하는 총 크레딧" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "사용된 크레딧 수" } }, "description": "크레딧 사용 정보" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대 분류(어린이/청소년/청년/중년/노년)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별 분류(남성/여성)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "사용 가능한 감정이 있는 지원되는 TTS 모델 목록(예: [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유한 보이스 식별자. 기본 제공 보이스는 `tc_` prefix (예: `tc_60e5426de8b95f1d3000d7b5`), `POST /v1/voices/clone` 으로 만든 커스텀 보이스는 `uc_` prefix 를 사용합니다. 본인이 소유한 `uc_` 보이스도 `/v2/voices` 응답에 포함됩니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "이 보이스가 적합한 사용 사례 카테고리 목록\n\n## 보이스 이름 참고\n\n`voice_name` 필드는 영문으로만 반환됩니다. 아래 목록에서 한국어 표기를 확인하실 수 있습니다.\n\n
\n보이스 이름 목록 펼치기 (542개)\n\n| 영어 | 한국어 | 영어 | 한국어 | 영어 | 한국어 |\n| --- | --- | --- | --- | --- | --- |\n| Aaron | 에런 | Inhye | 인혜 | Piljae | 필재 |\n| Abigail | 아비게일 | Inseong | 인성 | Pyeonghwa | 평화 |\n| Ae-ran | 애란 | Instructor Han | 한유격 교관 | Rachel | 레이첼 |\n| Ael | 아엘 | Insun | 인선 | Ravi | 라비 |\n| Agatha | 아가사 | Jabbaba | 자바바 | Rayeon | 라연 |\n| Aiden | 에이든 | Jack | 잭 | Rebecca | 레베카 |\n| Alena | 알레나 | Jack-o’-Lantern | 잭오랜턴 | Reporter Bona | 다보나 기자 |\n| Alex | 알렉스 | Jackson | 잭슨 | Reporter Catalina | 카탈리나 기자 |\n| Amber | 엠버 | Jaeho | 재호 | Reporter Jinsub | 송진섭 기자 |\n| Anchor Hwa | 김경화 앵커 | Jaehun | 재훈 | Reporter Kang | 강수정 기자 |\n| Angela | 안젤라 | Jaejun | 재준 | Reporter Lee | 이승주 기자 |\n| Anja | 안냐 | Jaekyung | 재경 | Reporter Parkchan | 박찬 기자 |\n| Annabelle the Ghost | 귀신 애나벨 | Jaesun | 재선 | Rex | 렉스 |\n| Annie | 애니 | JaeYi | 재이 | Riley | 라일리 |\n| April | 에이프릴 | Jaeyoung | 재영 | Risan Ji | 지리산 |\n| Arang | 아랑 | Jain | 자인 | Rita | 리타 |\n| Ari | 아리 | Jake | 제이크 | River | 리버 |\n| Arin | 아린 | Janet | 재닛 | Robert | 로버트 |\n| Athena | 아테나 | Jangho | 장호 | Robo | 로보 |\n| Audrey | 오드리 | Jangwoon | 장운 | Romi | 로미 |\n| Avery | 에이버리 | Jechan | 제찬 | Ron | 론 |\n| Avong | 아봉 | Jeff | 제프 | Roro | 로로 |\n| Azzi | 아찌 | Jenna | 제나 | Rowoon | 로운 |\n| Babilon | 바빌론 | Jennifer | 제니퍼 | Royce | 로이스 |\n| Bell | 벨 | Jeong Choi | 최정 | Rudolph | 루돌프 |\n| Ben | 벤 | Jeongah | 정아 | Ruri | 루리 |\n| Benny | 베니 | JeongJin | 정진 | Rusty | 러스티 |\n| Beri | 베리 | Jeongseob | 정섭 | Ryan | 라이언 |\n| Billie | 빌리 | Jerome | 제롬 | Ryueun | 류은 |\n| Bomi | 이보미 | Jewby | 쥬비 | Sabrina the Witch | 마녀 사브리나 |\n| Bonggyu | 봉규 | Jian | 지안 | Saeron | 새론 |\n| Bongman Kim | 김봉만 | Jicheol | 지철 | Salty Chan-gu | 삐뚤어진 찬구 |\n| Bono | 보노 | Jihee | 지희 | Samantha | 사만다 |\n| Bora | 보라 | Jihoon | 지훈 | Sammy | 새미 |\n| Brad | 브래드 | Jihyun | 지현 | Sangdo | 상도 |\n| Bruce | 브루스 | Jimmy | 지미 | Sanghoon | 상훈 |\n| Buddy | 버디 | Jin | 진 | Sangwoo | 상우 |\n| Bumsoo | 범수 | Jinhan | 진한 | Santa | 산타 |\n| Buttaguy | 빠다가이 | Jinhang Kim | 김진행 | Santa Claus | 산타클로스 |\n| Caitlyn | 케이틀린 | Jinhee | 진희 | Santa Reporter | VJ 싼타 |\n| Callan | 칼란 | Jinhyuk | 진혁 | Sara | 세라 |\n| Camila | 카밀라 | Jinseo | 진서 | Sean | 션 |\n| Captain Bill | 캡틴 빌 | Jinung | 진웅 | SeHee | 세희 |\n| Carl | 칼 | Jinwoo | 진우 | Seheon | 세헌 |\n| Carlos | 카를로스 | Jiwoo | 지우 | Sejin | 세진 |\n| Carol | 캐롤 | Jiyoon | 지윤 | Sena | 세나 |\n| Catherine | 캐서린 | Jiyoung | 지영 | Seohee | 서희 |\n| Chad | 채드 | Jodie | 조디 | Seojin | 서진 |\n| Chaeah | 채아 | Jolly | 졸리 | Seojoon | 서준 |\n| Chan-gu | 찬구 | Jongdae | 종대 | Seok Choi | 최석 |\n| Changbae | 창배 | Jooeun | 주은 | SeokPil | 한석필 |\n| Changhee | 창희 | Joonghyun | 중현 | Seokpyo | 석표 |\n| Changmin | 창민 | Joongsik | 중식 | Seolhwa | 설화 |\n| Changsu | 박창수 | Joonkyu | 준규 | Seonha | 선하 |\n| Chanhyuk | 찬혁 | Joshua | 조슈아 | Seoyeon | 서연 |\n| Charlotte | 샬롯 | Juha | 주하 | Seoyoon | 서윤 |\n| Chase | 체이스 | Juho | 주호 | Seungah | 승아 |\n| Cheolhoon | 철훈 | Jungbong | 정봉 | Seungheon | 승헌 |\n| Cheolyong | 리철용 | Junghee | 정희 | Seungho | 승호 |\n| Cherry | 체리 | Jungjae | 정재 | Seunghwa | 승화 |\n| Chester | 체스터 | Jungmin | 정민 | Seungjae | 승재 |\n| Chiho | 치호 | Jungseok | 정석 | Seungmoon | 승문 |\n| Chloe | 클로이 | Jungsoon | 정순 | Seungwon | 승원 |\n| Choyeon | 강초연 교관 | Jungwon | 정원 | Seungyeon | 승연 |\n| Chungah | 청아 | Junhee | 준희 | Sewoo | 세우 |\n| Chunsik Kang | 강춘식 기자 | Junho | 준호 | Shana | 샤나 |\n| Claire | 클레어 | JunKi | 준기 | Sheriff Kim | 김반장 |\n| Classic Narrator | 미스타 변사 | Junsang | 준상 | Shinhe | 신혜 |\n| Cole | 콜 | Junseong | 준성 | Shinwook | 신욱 |\n| Cox | 콕스 | Justice Roh | 정의로 | Shotgun | 샷건 |\n| Cyrus | 사이러스 | Justin | 저스틴 | Simon | 사이먼 |\n| Dabin | 다빈 | Juwon | 주원 | Sindarin | 신다린 |\n| Daegil | 대길 | Juyoung | 주영 | Sio | 시오 |\n| Dahee | 다희 | K-Santa | 산타 할아버지 | Siwon | 시원 |\n| Dahyeon | 다현 | Kangil | 강일 | Siwoo | 시우 |\n| Damian | 데이미언 | Kanno | 칸노 | Siyeon | 시연 |\n| Dan | 댄 | Katie | 케이티 | Skylar | 스카일러 |\n| Dana | 다나 | Kelly | 켈리 | Slushy | 슬러시 |\n| Dasom | 다솜 | Kelsey | 켈시 | Smoke | 스모크 |\n| David | 데이비드 | Kevin | 케빈 | Sohye | 소혜 |\n| Dean | 딘 | Keybo | 키보 | Sojin | 소진 |\n| Deokhwan | 덕환 | Kijang Kim | 김기장 | Soobin | 수빈 |\n| Dohan | 도한 | Killian the Vampire | 뱀파이어 킬리언 | Sookhee | 숙희 |\n| Dohee | 도희 | Kiseob | 기섭 | Sooni | 순이 |\n| Dohyun | 도현 | Klip Kim | 클립 킴 | Sophia | 소피아 |\n| Dollar Jr. | 달러 주니어 | Koombo | 쿰보 | Sora | 소라 |\n| Doug | 더그 | Kristen | 크리스틴 | Soye | 소예 |\n| Doughnut | 도넛 | Kukhee | 국희 | Soyi | 소이 |\n| DU5T | 더스트 | Kwonil | 권일 | Soyoung | 소영 |\n| Duckchun | 덕춘 | Kyumin | 규민 | Soyul | 소율 |\n| Duke | 듀크 | Kyungho | 경호 | Spice | 스파이스 |\n| Dukgu | 덕구 | Kyungsoo | 경수 | Sportscaster Kang | 강수정 캐스터 |\n| Duman | 두만 | Kyungsook | 경숙 | Sportscaster Tony | 이영광 캐스터 |\n| Dupil | 곽두필 | Lady Cho | 단희빈 | Starling | 스탈링 |\n| DVZY | 데이지 | Lala | 라라 | Stephanie | 스테파니 |\n| Dylan | 딜런 | Lamie | 라미 | Storyteller Jinsub | 송진섭 스토리텔러 |\n| E-seller Sherri | 쇼린이 | Landon | 랜던 | Sua | 수아 |\n| Echo | 에코 | Larry | 래리 | Suho | 수호 |\n| Edward | 에드워드 | Leo | 레오 | Suji | 수지 |\n| Elias | 엘리아스 | Liam | 리암 | Sujin | 수진 |\n| Elise | 엘리스 | Lindsay | 린지 | Sullock Hong | 홍설록 |\n| Ella | 엘라 | Liz | 리즈 | Sumin | 수민 |\n| Eman | 이만 | Lloyd | 로이드 | Sungbae | 성배 |\n| Emma | 엠마 | Logan | 로건 | Sunggyu | 성규 |\n| Eunbin | 은빈 | Lucille | 루실 | Sungho | 성호 |\n| Eunchae | 은채 | Luna | 루나 | Sunghoon | 성훈 |\n| Eunha | 은하 | Lydia | 리디아 | Sunghyun | 성현 |\n| Eunsol | 은솔 | Maddie | 매디 | Sungjun | 홈쇼핑 성준 |\n| Frankenstein | 프랑켄슈타인 | Maisie | 메이시 | Sungkwon | 성권 |\n| Furnando | 터르난도 | Margaret | 마가렛 | Sungtae | 성태 |\n| Gaeul | 가을 | Margot | 마고 | Sungwook | 성욱 |\n| Gahee | 가희 | Matthew | 매튜 | Sunyoung | 선영 |\n| George | 조지 | MBTI EF (F) | MBTI EF 여 | Suyoon | 수윤 |\n| GeumHee | 금희 | MBTI EF (M) | MBTI EF 남 | Sylvia | 실비아 |\n| Geunhyeok | 근혁 | MBTI ET (F) | MBTI ET 여 | Taebaek | 장태백 |\n| Geunseok | 근석 | MBTI ET (M) | MBTI ET 남 | Taeji | 태지 |\n| Geunwoo | 근우 | MBTI IF (F) | MBTI IF 여 | Taejoong | 태중 |\n| Geunyeong | 근영 | MBTI IF (M) | MBTI IF 남 | Taemin | 태민 |\n| Ggami | 까미 | MBTI IT (F) | MBTI IT 여 | Taesub | 태섭 |\n| Ggomi | 꼬미 | MBTI IT (M) | MBTI IT 남 | Taewoo | 태우 |\n| Glenda | 글렌다 | MC Kong | MC콩 | Teal | 틸 |\n| Goat Kim | 김고트 | MC TypeCast | MC 타캐 | Tessa | 테사 |\n| Gongchul | 공철 | Mia | 미아 | Tian | 티안 |\n| Gowoon | 고운 | Michael | 마이클 | Tim | 팀 |\n| Grace | 그레이스 | Mija | 오미자 | Tina | 티나 |\n| Graham | 그레이엄 | Mijin | 미진 | Toby | 토비 |\n| Gunseok | 건석 | Mikyung | 미경 | Tommy | 토미 |\n| Gunwoo | 건우 | Millie | 밀리 | Tyson | 타이슨 |\n| Guri | 구리 | Minchae | 민채 | UiChan | 의찬이 |\n| Gus | 거스 | Minji | 민지 | Uncle Hank | 행크 |\n| Ha Eun | 하은 | Minjoon | 민준 | Valerie | 밸러리 |\n| Haejun | 해준 | Minju | 민주 | Valkyrie | 발키리 |\n| Haerang | 해랑 | Minjung | 민정 | Vanessa | 바네사 |\n| Hailey | 헤일리 | Minsang | 민상 | Verna | 베르나 |\n| Hajun | 하준 | Minsu | 민수 | Victoria | 빅토리아 |\n| Hakchul | 학철 | Mio | 미오 | Viktor | 빅토르 |\n| Hamchu | 햄쮸 | Miran Choi | 최미란 | Vincent | 빈센트 |\n| Han Taesung Caster | 한태성 캐스터 | Mirine | 미리내 | Vivien | 비비안 |\n| Hana | 하나 | Miseon | 미선 | Wade | 웨이드 |\n| Hangyeol | 한결 | Miso | 미소 | Walter | 월터 |\n| Hanjun | 한준 | Mister Gop | 미스터 갑 | Wangkwon | 왕권 |\n| Hanna | 한나 | Monggun | 몽군 | Weather Reporter Sky | 하늘 캐스터 |\n| Hans | 한스 | Moonjung | 문정 | West | 웨스트 |\n| Hansol | 한솔 | Mooyeol | 무열 | Wonho | 원호 |\n| Hanyoung | 한영 | Morgan | 모건 | Wonkyung | 원경 |\n| Harin | 하린 | Moru | 머루 | Wonwoo | 원우 |\n| Harper | 하퍼 | Mrs. Claus | 미세스 클로스 | Wooju | 우주 |\n| Hayul | 하율 | Munseok | 문석 | Woosung | 우성 |\n| Heejoon | 희준 | Munsu | 문수 | Xavier | 자비에르 |\n| Helena | 헬레나 | Myeonghee | 명희 | Yeeun | 예은 |\n| Henry | 헨리 | Myungil | 명일 | Yejin | 예진 |\n| Homun Sim | 심호문 | Myungjoo | 명주 | Yejoon | 예준 |\n| Hoon | 훈 | Naeun | 나은 | YeLin | 예린 |\n| Hosik | 호식 | Najin | 나진 | Yena | 예나 |\n| Hosun | 호선 | Namjoon | 남준 | Yeonah | 연아 |\n| Hoyoung | 호영 | Nana | 나나 | Yeonggeol | 영걸 |\n| Hugh | 휴 | Nari | 개나리 | Yeonhwa | 연화 |\n| Hwimin | 휘민 | Nathan | 네이선 | Yeonja | 연자 |\n| Hyejung | 혜정 | Neel | 닐 | Yeonsuh | 연서 |\n| Hyelee | 혜리 | Neoguard | 네오가드 | Yeonwoo | 연우 |\n| Hyemin | 혜민 | News anchor Jinsub | 송진섭 앵커 | Yeseul | 예슬 |\n| Hyena | 혜나 | Newscaster John | 존 앵커 | Yongsik | 용식이 |\n| Hyeongjin | 형진 | Nia | 니아 | Yoonseo | 윤서 |\n| Hyera | 혜라 | Noa | 노아 | Yoonsung | 윤성 |\n| Hyesu | 홈쇼핑 혜수 | Noel | 노엘 | Younggil | 영길 |\n| Hyewon | 혜원 | Noeul | 노을 | Younghee | 영희 |\n| Hyun | 현 | Norah | 노라 | Younghwan | 영환 |\n| Hyunji | 현지 | Nova | 노바 | Youngji | 영지 |\n| Hyunjin | 현진 | Old radio | 레디오 | Youngkyu | 영규 |\n| Hyunju | 현주 | Oliver | 올리버 | Youngmok | 영목 |\n| Hyunkyung | 현경 | Olivia | 올리비아 | Yubin | 유빈 |\n| Hyunmin | 현민 | Oscar | 오스카 | Yujin | 유진 |\n| Hyunseung | 현승 | Owen | 오언 | Yumi | 유미 |\n| HyunWoo | 현우 | P-0150N | 포이즌 | Yumin | 유민 |\n| Ian | 이안 | Pacang | 파캉 | Yunbin | 윤빈 |\n| Icarus | 이카루스 | Paige | 페이지 | Yunjeong | 윤정 |\n| Igyeom | 이겸 | Pangpang | 팡팡 | Yura | 유라 |\n| Ijun | 이준 | Patricia | 패트리샤 | Yuri | 유리 |\n| Ilho | 일호 | Patrick | 패트릭 | Yuseong | 유성 |\n| Ina | 이나 | Peter | 피터 | Zoey | 조이 |\n| Inhwa | 인화 | Philip | 필립 | | |\n\n
\n" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "사람이 읽을 수 있는 보이스 이름" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입 — `original` 은 타입캐스트가 기본 제공하는 스톡 보이스, `custom` 은 사용자가 업로드/클론한 보이스입니다." } }, "description": "모델별로 그룹화된 감정과 향상된 메타데이터가 있는 V2 보이스 응답 모델" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대입니다." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별(`male` 또는 `female`)입니다." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "지원하는 TTS 모델과 감정 목록입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유 보이스 식별자입니다. 기본 보이스는 `tc_`, 커스텀 보이스는 `uc_`로 시작합니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "추천 사용 사례 목록입니다." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "`{\"eng\": \"Daejin\", \"kor\": \"대진\"}` 같은 다국어 보이스 이름입니다." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입(`original` 또는 `custom`)입니다." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "제공되는 경우 사용할 수 있는 미리듣기 오디오 URL입니다." } }, "description": "다국어 이름과 미리듣기 URL을 포함한 보이스 정보입니다." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "API 플랜.\n\n사용 가능한 값:\n- **free**: 제한된 크레딧의 무료 등급\n- **lite**: 중간 수준 크레딧의 라이트 플랜\n- **plus**: 높은 크레딧의 플러스 플랜\n- **custom**: 커스텀 엔터프라이즈 플랜" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "음성 합성에 사용할 TTS 모델 버전. 다양한 모델은 다양한 기능과 품질 수준을 제공합니다.\n\n사용 가능한 모델:\n- **ssfm-v30**: 향상된 플로우와 추가 감정 프리셋이 있는 최신 모델(권장)\n- **ssfm-v21**: 검증된 신뢰성과 일관된 품질을 갖춘 안정적인 모델\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS 모델 버전(예: ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "이 모델에서 지원되는 감정 목록" } }, "description": "버전 및 지원되는 감정을 포함한 모델 정보" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "영어 보이스 이름입니다." }, "kor": { "type": "string", "title": "Kor", "description": "한국어 보이스 이름입니다." } }, "description": "ISO 639-3 언어 코드를 키로 사용하는 다국어 보이스 이름입니다." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "보이스 타입 분류.\n\n- `original` — 타입캐스트가 기본 제공하는 스톡 보이스로, 모든 계정에서 사용 가능합니다.\n- `custom` — 사용자가 본인의 샘플을 업로드하거나 클로닝해서 만든 보이스입니다." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "성별 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **male**: 남성 보이스\n- **female**: 여성 보이스\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "음성 합성에 사용 가능한 감정 프리셋. 각 감정은 생성된 음성의 톤, 속도, 표현력에 영향을 줍니다.\n\n**ssfm-v21 지원 감정 (4종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n\n**ssfm-v30 지원 감정 (7종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n- whisper: 부드럽고 조용한 말\n- toneup: 더 높은 톤 강조\n- tonedown: 더 낮은 톤 강조\n\n/v2/voices API 응답을 통해 각 음성에 사용 가능한 감정을 확인하세요.\n" }, "SmartPrompt": { "type": "object", "title": "스마트 프롬프트 (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "TTSRequest의 `text` 필드 이후에 오는 텍스트. 감정 추론을 위한 전방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 전환을 예측하는 데 도움\r\n- 다음 컨텍스트가 없으면 비워 둡니다\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "프롬프트 유형을 식별하는 판별자 필드. 컨텍스트 인식 감정 추론을 위해 \"smart\"로 설정해야 합니다.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "TTSRequest의 `text` 필드 이전에 오는 텍스트. 감정 추론을 위한 후방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 빌드업과 컨텍스트를 이해하는 데 도움\r\n- 이전 컨텍스트가 없으면 비워 둡니다\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "프리셋 프롬프트 (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "프롬프트 유형을 식별하는 판별자 필드. 프리셋 기반 감정 제어를 위해 \"preset\"으로 설정해야 합니다.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "생성된 음성에 적용할 감정 프리셋.\r\n\r\n지원되는 감정: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "생성된 음성의 감정 표현 강도를 제어합니다.\r\n\r\n- 0.0: 완전히 중립적, 감정 색채 없음\r\n- 0.5: 미묘한 감정 힌트\r\n- 1.0: 표준 감정 표현(기본값)\r\n- 1.5: 강한 감정 강조\r\n- 2.0: 최대 강도, 매우 표현력 있음\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "콘텐츠 유형 필터링을 위한 보이스 사용 사례 카테고리. 각 보이스는 특정 콘텐츠 유형에 대한 적합성을 나타내는 하나 이상의 사용 사례로 태그됩니다.\n\n**사용 가능한 사용 사례:**\n- **Announcer**: 공공 발표 및 프레젠테이션\n- **Anime**: 애니메이션 보이스\n- **Audiobook**: 장문 내레이션 및 스토리텔링\n- **Conversational**: 챗봇 및 대화형 AI\n- **Documentary**: 다큐멘터리 내레이션 및 해설\n- **E-learning**: 교육 콘텐츠 및 튜토리얼\n- **Rapper**: 랩 및 음악 퍼포먼스\n- **Game**: 비디오 게임 보이스 및 내레이션\n- **Tiktok/Reels**: SNS 숏츠\n- **News**: 뉴스 방송\n- **Podcast**: 방송 및 팟캐스트 제작\n- **Voicemail**: IVR 시스템 및 음성 비서\n- **Ads**: 광고 및 홍보 콘텐츠\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "문제를 설명하는 오류 메시지" }, "message": { "type": "string", "description": "구조화된 오류에 대한 설명" }, "error_code": { "type": "string", "description": "구조화된 오류를 식별하는 코드" } }, "description": "기존 오류와 유효성 검사 오류는 `detail`을 사용하며, 구조화된 오류는 `error_code`와 `message`를 사용합니다." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "`status`가 `failed`일 때의 실패 사유입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "source": { "type": "string", "title": "Source", "description": "생성 방식(`instant` 또는 `professional`)입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "UTC 기준 생성 시각입니다." } }, "description": "인증된 계정이 소유한 커스텀 보이스입니다." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "추천 관련도 점수. 값이 높을수록 검색어와 더 잘 맞는 후보입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "`tc_` prefix 가 붙은 타입캐스트 보이스 식별자. 텍스트 음성 변환 요청의 `voice_id` 로 사용할 수 있습니다." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "사람이 읽을 수 있는 보이스 이름." } }, "description": "`GET /v1/voices/recommendations` 응답의 보이스 추천 후보. 관련도 순서로 정렬됩니다." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "커스텀 보이스의 현재 생성 또는 학습 상태입니다." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름 (1~30자)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "보이스가 클로닝된 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "`uc_` prefix 가 붙은 커스텀 보이스 식별자. `POST /v1/text-to-speech` 등 `voice_id` 를 받는 엔드포인트에 그대로 사용할 수 있습니다." } }, "description": "`POST /v1/voices/clone` 응답 — 퀵 클로닝으로 생성된 커스텀 보이스 메타데이터." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "세그먼트 구분값입니다. 항상 `pause`입니다." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "초 단위 쉼 길이입니다. 개별 쉼은 최대 10초, 모든 쉼의 합은 최대 60초입니다.", "exclusiveMinimum": 0 } }, "description": "크레딧 차감 없이 삽입되는 무음 구간입니다.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 단어 단위 정렬 구간." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "현재 구독 플랜명" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "사용 제한 정보" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "크레딧 사용 정보" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "구독 정보 응답 모델" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호/공백 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 문자 단위 정렬 구간." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." } }, "description": "퀵 클로닝 또는 프리미엄 클로닝 생성 요청 결과입니다." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "base64 로 인코딩된 오디오 바이트. `audio_format` 확장자로 디코딩해 파일로 저장할 수 있습니다." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "단어 단위 타임스탬프(문장부호 포함). 요청이 `granularity=char` 일 때는 `null`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "문자 단위 타임스탬프(문장부호와 공백 포함). 요청이 `granularity=word` 일 때는 `null`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "`audio` 필드의 오디오 인코딩 포맷 — `wav` 또는 `mp3` (요청의 `output.audio_format` 에 따라 결정)." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "생성된 오디오의 길이(초)." } }, "description": "TTS 생성 + 타임스탬프 정렬 통합 응답." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "오디오 샘플. WAV 또는 MP3, 최대 25MB, 5초 이상 150초 이하.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "보이스 이름 (1~30자)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "클로닝할 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." } }, "description": "퀵 클로닝 요청을 위한 multipart 본문." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "파일", "format": "binary", "description": "25 MiB 이하이면서 길이가 5초 이상 150초 이하인 WAV 또는 MP3 음성 파일 1개입니다.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "상품 내레이터", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." } }, "description": "퀵 클로닝용 multipart 요청 본문입니다." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "커스텀 보이스 이름", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "파일", "description": "학습에 사용할 WAV 또는 MP3 음성 파일입니다. 하나의 파일을 업로드할 수 있습니다.\r\n\r\n**업로드 제한**\r\n\r\n* WAV 또는 MP3 파일 1개\r\n* 파일 크기: 1 GiB 이하\r\n* 길이: 5분 이상 3시간 이하\r\n* 샘플레이트: 16 kHz 이상\r\n\r\n최상의 결과를 위해 다음 조건을 충족하는 오디오를 권장합니다.\r\n\r\n* 생성된 보이스가 재현하기를 원하는 말투와 최대한 가깝게 녹음해 주세요.\r\n* 배경 소음이 없는 조용한 환경에서 녹음해 주세요.\r\n* 한 명의 화자 목소리만 포함해 주세요.\r\n* `language` 필드에 지정한 언어로 녹음해 주세요.\r\n* 오디오 길이가 길수록 생성되는 보이스의 품질이 향상됩니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." }, "language": { "type": "string", "title": "Language", "example": "kor", "description": "`kor`, `eng` 같은 ISO 639-3 언어 코드입니다." } }, "description": "프리미엄 클로닝용 multipart 요청 본문입니다." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "인증을 위한 API 키. 타입캐스트 API 콘솔에서 API 키를 생성할 수 있습니다." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 보이스 추천 > 텍스트 묘사를 기반으로 조건에 맞는 타입캐스트 보이스를 추천하는 API입니다. `voice_id`를 직접 조회할 필요 없이, 원하는 스타일·분위기·언어·사용 사례 등의 키워드나 문장으로 보이스를 검색할 수 있습니다. 응답은 추천 점수 순서로 정렬되며, 상위 후보의 `voice_id`를 `POST /v1/text-to-speech` 같은 텍스트 음성 변환 엔드포인트에 바로 전달할 수 있습니다. 응답에는 `voice_id`, `voice_name`, `score`만 포함됩니다. 추천된 보이스의 지원 모델, 감정, 성별, 나이대, 사용 사례 같은 상세 메타데이터가 필요하면 `GET /v2/voices` 또는 `GET /v2/voices/{voice_id}`를 함께 호출하세요. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "프로덕션 서버" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/voices/recommendations": { "get": { "tags": [ "Voices" ], "x-mint": { "href": "/ko/api-reference/voices/recommend-voices" }, "summary": "보이스 추천", "responses": { "200": { "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/RecommendedVoice" }, "title": "Response Recommend Voices V1 Voices Recommendations Get", "maxItems": 10 }, "example": [ { "score": 0.92, "voice_id": "tc_60e5426de8b95f1d3000d7b5", "voice_name": "Olivia" }, { "score": 0.87, "voice_id": "tc_62a8975e695ad26f7fb514d1", "voice_name": "Emma" } ] } }, "description": "Success - Returns recommended voices sorted by score" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Unauthorized - Invalid or missing API key" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" }, "example": { "detail": [ { "loc": [ "query", "query" ], "msg": "String should have at most 500 characters", "type": "string_too_long" } ] } } }, "description": "Validation Error - Invalid request parameters" }, "500": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "An unexpected error occurred" } } }, "description": "Internal Server Error - Voice recommendation failed" } }, "parameters": [ { "in": "query", "name": "query", "schema": { "type": "string", "title": "Query", "maxLength": 500, "minLength": 1, "description": "텍스트 묘사. 원하는 스타일, 분위기, 언어, 사용 사례, 콘텐츠 맥락을 키워드나 문장으로 설명합니다." }, "example": "warm female voice for product tutorial", "required": true, "description": "텍스트 묘사. 원하는 스타일, 분위기, 언어, 사용 사례, 콘텐츠 맥락을 키워드나 문장으로 설명합니다." }, { "in": "query", "name": "count", "schema": { "type": "integer", "title": "Count", "default": 5, "maximum": 10, "minimum": 1, "description": "필터링 후 반환할 추천 보이스 최대 개수. 1~10 사이의 값이어야 합니다. 조건에 맞는 후보가 충분하지 않으면 `count`보다 적은 수의 보이스가 반환될 수 있습니다." }, "example": 5, "required": false, "description": "필터링 후 반환할 추천 보이스 최대 개수. 1~10 사이의 값이어야 합니다. 조건에 맞는 후보가 충분하지 않으면 `count`보다 적은 수의 보이스가 반환될 수 있습니다." } ], "description": "텍스트 묘사를 기반으로 조건에 맞는 타입캐스트 보이스를 추천하는 API입니다.\n\n`voice_id`를 직접 조회할 필요 없이, 원하는 스타일·분위기·언어·사용 사례 등의 키워드나 문장으로 보이스를 검색할 수 있습니다. 응답은 추천 점수 순서로 정렬되며, 상위 후보의 `voice_id`를 `POST /v1/text-to-speech` 같은 텍스트 음성 변환 엔드포인트에 바로 전달할 수 있습니다.\n\n응답에는 `voice_id`, `voice_name`, `score`만 포함됩니다. 추천된 보이스의 지원 모델, 감정, 성별, 나이대, 사용 사례 같은 상세 메타데이터가 필요하면 `GET /v2/voices` 또는 `GET /v2/voices/{voice_id}`를 함께 호출하세요.", "operationId": "recommend_voices_v1_voices_recommendations_get", "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request GET \\\n --url 'https://api.typecast.ai/v1/voices/recommendations?query=warm%20female%20voice%20for%20product%20tutorial&count=5' \\\n --header 'X-API-KEY: '\n" }, { "lang": "Python", "label": "Python (requests)", "source": "import requests\n\nresponse = requests.get(\n \"https://api.typecast.ai/v1/voices/recommendations\",\n headers={\"X-API-KEY\": \"\"},\n params={\n \"query\": \"warm female voice for product tutorial\",\n \"count\": 5,\n },\n timeout=30,\n)\nresponse.raise_for_status()\n\nrecommendations = response.json()\nif recommendations:\n print(recommendations[0][\"voice_id\"])\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "허용되는 최대 동시 요청 수" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "전체 커스텀 보이스 슬롯 한도 (professional 포함)" }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "사용 제한 정보" }, "Prompt": { "title": "프롬프트 (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "적용할 감정 프리셋.\r\n\r\nssfm-v21 지원 감정: normal, happy, sad, angry\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "example": 1, "description": "감정 표현 강도 제어(0.0~2.0).\r\n\r\n- 0.0: 완전히 중립적\r\n- 1.0: 표준 표현(기본값)\r\n- 2.0: 최대 강도\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "연령대 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **child**: 어린이 보이스(12세 미만)\n- **teenager**: 청소년 보이스(13-19세)\n- **young_adult**: 청년 보이스(20-35세)\n- **middle_age**: 중년 보이스(36-60세)\n- **elder**: 노년 보이스(60세 이상)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "플랜에서 기본으로 제공하는 총 크레딧" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "사용된 크레딧 수" } }, "description": "크레딧 사용 정보" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대 분류(어린이/청소년/청년/중년/노년)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별 분류(남성/여성)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "사용 가능한 감정이 있는 지원되는 TTS 모델 목록(예: [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유한 보이스 식별자. 기본 제공 보이스는 `tc_` prefix (예: `tc_60e5426de8b95f1d3000d7b5`), `POST /v1/voices/clone` 으로 만든 커스텀 보이스는 `uc_` prefix 를 사용합니다. 본인이 소유한 `uc_` 보이스도 `/v2/voices` 응답에 포함됩니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "이 보이스가 적합한 사용 사례 카테고리 목록\n\n## 보이스 이름 참고\n\n`voice_name` 필드는 영문으로만 반환됩니다. 아래 목록에서 한국어 표기를 확인하실 수 있습니다.\n\n
\n보이스 이름 목록 펼치기 (542개)\n\n| 영어 | 한국어 | 영어 | 한국어 | 영어 | 한국어 |\n| --- | --- | --- | --- | --- | --- |\n| Aaron | 에런 | Inhye | 인혜 | Piljae | 필재 |\n| Abigail | 아비게일 | Inseong | 인성 | Pyeonghwa | 평화 |\n| Ae-ran | 애란 | Instructor Han | 한유격 교관 | Rachel | 레이첼 |\n| Ael | 아엘 | Insun | 인선 | Ravi | 라비 |\n| Agatha | 아가사 | Jabbaba | 자바바 | Rayeon | 라연 |\n| Aiden | 에이든 | Jack | 잭 | Rebecca | 레베카 |\n| Alena | 알레나 | Jack-o’-Lantern | 잭오랜턴 | Reporter Bona | 다보나 기자 |\n| Alex | 알렉스 | Jackson | 잭슨 | Reporter Catalina | 카탈리나 기자 |\n| Amber | 엠버 | Jaeho | 재호 | Reporter Jinsub | 송진섭 기자 |\n| Anchor Hwa | 김경화 앵커 | Jaehun | 재훈 | Reporter Kang | 강수정 기자 |\n| Angela | 안젤라 | Jaejun | 재준 | Reporter Lee | 이승주 기자 |\n| Anja | 안냐 | Jaekyung | 재경 | Reporter Parkchan | 박찬 기자 |\n| Annabelle the Ghost | 귀신 애나벨 | Jaesun | 재선 | Rex | 렉스 |\n| Annie | 애니 | JaeYi | 재이 | Riley | 라일리 |\n| April | 에이프릴 | Jaeyoung | 재영 | Risan Ji | 지리산 |\n| Arang | 아랑 | Jain | 자인 | Rita | 리타 |\n| Ari | 아리 | Jake | 제이크 | River | 리버 |\n| Arin | 아린 | Janet | 재닛 | Robert | 로버트 |\n| Athena | 아테나 | Jangho | 장호 | Robo | 로보 |\n| Audrey | 오드리 | Jangwoon | 장운 | Romi | 로미 |\n| Avery | 에이버리 | Jechan | 제찬 | Ron | 론 |\n| Avong | 아봉 | Jeff | 제프 | Roro | 로로 |\n| Azzi | 아찌 | Jenna | 제나 | Rowoon | 로운 |\n| Babilon | 바빌론 | Jennifer | 제니퍼 | Royce | 로이스 |\n| Bell | 벨 | Jeong Choi | 최정 | Rudolph | 루돌프 |\n| Ben | 벤 | Jeongah | 정아 | Ruri | 루리 |\n| Benny | 베니 | JeongJin | 정진 | Rusty | 러스티 |\n| Beri | 베리 | Jeongseob | 정섭 | Ryan | 라이언 |\n| Billie | 빌리 | Jerome | 제롬 | Ryueun | 류은 |\n| Bomi | 이보미 | Jewby | 쥬비 | Sabrina the Witch | 마녀 사브리나 |\n| Bonggyu | 봉규 | Jian | 지안 | Saeron | 새론 |\n| Bongman Kim | 김봉만 | Jicheol | 지철 | Salty Chan-gu | 삐뚤어진 찬구 |\n| Bono | 보노 | Jihee | 지희 | Samantha | 사만다 |\n| Bora | 보라 | Jihoon | 지훈 | Sammy | 새미 |\n| Brad | 브래드 | Jihyun | 지현 | Sangdo | 상도 |\n| Bruce | 브루스 | Jimmy | 지미 | Sanghoon | 상훈 |\n| Buddy | 버디 | Jin | 진 | Sangwoo | 상우 |\n| Bumsoo | 범수 | Jinhan | 진한 | Santa | 산타 |\n| Buttaguy | 빠다가이 | Jinhang Kim | 김진행 | Santa Claus | 산타클로스 |\n| Caitlyn | 케이틀린 | Jinhee | 진희 | Santa Reporter | VJ 싼타 |\n| Callan | 칼란 | Jinhyuk | 진혁 | Sara | 세라 |\n| Camila | 카밀라 | Jinseo | 진서 | Sean | 션 |\n| Captain Bill | 캡틴 빌 | Jinung | 진웅 | SeHee | 세희 |\n| Carl | 칼 | Jinwoo | 진우 | Seheon | 세헌 |\n| Carlos | 카를로스 | Jiwoo | 지우 | Sejin | 세진 |\n| Carol | 캐롤 | Jiyoon | 지윤 | Sena | 세나 |\n| Catherine | 캐서린 | Jiyoung | 지영 | Seohee | 서희 |\n| Chad | 채드 | Jodie | 조디 | Seojin | 서진 |\n| Chaeah | 채아 | Jolly | 졸리 | Seojoon | 서준 |\n| Chan-gu | 찬구 | Jongdae | 종대 | Seok Choi | 최석 |\n| Changbae | 창배 | Jooeun | 주은 | SeokPil | 한석필 |\n| Changhee | 창희 | Joonghyun | 중현 | Seokpyo | 석표 |\n| Changmin | 창민 | Joongsik | 중식 | Seolhwa | 설화 |\n| Changsu | 박창수 | Joonkyu | 준규 | Seonha | 선하 |\n| Chanhyuk | 찬혁 | Joshua | 조슈아 | Seoyeon | 서연 |\n| Charlotte | 샬롯 | Juha | 주하 | Seoyoon | 서윤 |\n| Chase | 체이스 | Juho | 주호 | Seungah | 승아 |\n| Cheolhoon | 철훈 | Jungbong | 정봉 | Seungheon | 승헌 |\n| Cheolyong | 리철용 | Junghee | 정희 | Seungho | 승호 |\n| Cherry | 체리 | Jungjae | 정재 | Seunghwa | 승화 |\n| Chester | 체스터 | Jungmin | 정민 | Seungjae | 승재 |\n| Chiho | 치호 | Jungseok | 정석 | Seungmoon | 승문 |\n| Chloe | 클로이 | Jungsoon | 정순 | Seungwon | 승원 |\n| Choyeon | 강초연 교관 | Jungwon | 정원 | Seungyeon | 승연 |\n| Chungah | 청아 | Junhee | 준희 | Sewoo | 세우 |\n| Chunsik Kang | 강춘식 기자 | Junho | 준호 | Shana | 샤나 |\n| Claire | 클레어 | JunKi | 준기 | Sheriff Kim | 김반장 |\n| Classic Narrator | 미스타 변사 | Junsang | 준상 | Shinhe | 신혜 |\n| Cole | 콜 | Junseong | 준성 | Shinwook | 신욱 |\n| Cox | 콕스 | Justice Roh | 정의로 | Shotgun | 샷건 |\n| Cyrus | 사이러스 | Justin | 저스틴 | Simon | 사이먼 |\n| Dabin | 다빈 | Juwon | 주원 | Sindarin | 신다린 |\n| Daegil | 대길 | Juyoung | 주영 | Sio | 시오 |\n| Dahee | 다희 | K-Santa | 산타 할아버지 | Siwon | 시원 |\n| Dahyeon | 다현 | Kangil | 강일 | Siwoo | 시우 |\n| Damian | 데이미언 | Kanno | 칸노 | Siyeon | 시연 |\n| Dan | 댄 | Katie | 케이티 | Skylar | 스카일러 |\n| Dana | 다나 | Kelly | 켈리 | Slushy | 슬러시 |\n| Dasom | 다솜 | Kelsey | 켈시 | Smoke | 스모크 |\n| David | 데이비드 | Kevin | 케빈 | Sohye | 소혜 |\n| Dean | 딘 | Keybo | 키보 | Sojin | 소진 |\n| Deokhwan | 덕환 | Kijang Kim | 김기장 | Soobin | 수빈 |\n| Dohan | 도한 | Killian the Vampire | 뱀파이어 킬리언 | Sookhee | 숙희 |\n| Dohee | 도희 | Kiseob | 기섭 | Sooni | 순이 |\n| Dohyun | 도현 | Klip Kim | 클립 킴 | Sophia | 소피아 |\n| Dollar Jr. | 달러 주니어 | Koombo | 쿰보 | Sora | 소라 |\n| Doug | 더그 | Kristen | 크리스틴 | Soye | 소예 |\n| Doughnut | 도넛 | Kukhee | 국희 | Soyi | 소이 |\n| DU5T | 더스트 | Kwonil | 권일 | Soyoung | 소영 |\n| Duckchun | 덕춘 | Kyumin | 규민 | Soyul | 소율 |\n| Duke | 듀크 | Kyungho | 경호 | Spice | 스파이스 |\n| Dukgu | 덕구 | Kyungsoo | 경수 | Sportscaster Kang | 강수정 캐스터 |\n| Duman | 두만 | Kyungsook | 경숙 | Sportscaster Tony | 이영광 캐스터 |\n| Dupil | 곽두필 | Lady Cho | 단희빈 | Starling | 스탈링 |\n| DVZY | 데이지 | Lala | 라라 | Stephanie | 스테파니 |\n| Dylan | 딜런 | Lamie | 라미 | Storyteller Jinsub | 송진섭 스토리텔러 |\n| E-seller Sherri | 쇼린이 | Landon | 랜던 | Sua | 수아 |\n| Echo | 에코 | Larry | 래리 | Suho | 수호 |\n| Edward | 에드워드 | Leo | 레오 | Suji | 수지 |\n| Elias | 엘리아스 | Liam | 리암 | Sujin | 수진 |\n| Elise | 엘리스 | Lindsay | 린지 | Sullock Hong | 홍설록 |\n| Ella | 엘라 | Liz | 리즈 | Sumin | 수민 |\n| Eman | 이만 | Lloyd | 로이드 | Sungbae | 성배 |\n| Emma | 엠마 | Logan | 로건 | Sunggyu | 성규 |\n| Eunbin | 은빈 | Lucille | 루실 | Sungho | 성호 |\n| Eunchae | 은채 | Luna | 루나 | Sunghoon | 성훈 |\n| Eunha | 은하 | Lydia | 리디아 | Sunghyun | 성현 |\n| Eunsol | 은솔 | Maddie | 매디 | Sungjun | 홈쇼핑 성준 |\n| Frankenstein | 프랑켄슈타인 | Maisie | 메이시 | Sungkwon | 성권 |\n| Furnando | 터르난도 | Margaret | 마가렛 | Sungtae | 성태 |\n| Gaeul | 가을 | Margot | 마고 | Sungwook | 성욱 |\n| Gahee | 가희 | Matthew | 매튜 | Sunyoung | 선영 |\n| George | 조지 | MBTI EF (F) | MBTI EF 여 | Suyoon | 수윤 |\n| GeumHee | 금희 | MBTI EF (M) | MBTI EF 남 | Sylvia | 실비아 |\n| Geunhyeok | 근혁 | MBTI ET (F) | MBTI ET 여 | Taebaek | 장태백 |\n| Geunseok | 근석 | MBTI ET (M) | MBTI ET 남 | Taeji | 태지 |\n| Geunwoo | 근우 | MBTI IF (F) | MBTI IF 여 | Taejoong | 태중 |\n| Geunyeong | 근영 | MBTI IF (M) | MBTI IF 남 | Taemin | 태민 |\n| Ggami | 까미 | MBTI IT (F) | MBTI IT 여 | Taesub | 태섭 |\n| Ggomi | 꼬미 | MBTI IT (M) | MBTI IT 남 | Taewoo | 태우 |\n| Glenda | 글렌다 | MC Kong | MC콩 | Teal | 틸 |\n| Goat Kim | 김고트 | MC TypeCast | MC 타캐 | Tessa | 테사 |\n| Gongchul | 공철 | Mia | 미아 | Tian | 티안 |\n| Gowoon | 고운 | Michael | 마이클 | Tim | 팀 |\n| Grace | 그레이스 | Mija | 오미자 | Tina | 티나 |\n| Graham | 그레이엄 | Mijin | 미진 | Toby | 토비 |\n| Gunseok | 건석 | Mikyung | 미경 | Tommy | 토미 |\n| Gunwoo | 건우 | Millie | 밀리 | Tyson | 타이슨 |\n| Guri | 구리 | Minchae | 민채 | UiChan | 의찬이 |\n| Gus | 거스 | Minji | 민지 | Uncle Hank | 행크 |\n| Ha Eun | 하은 | Minjoon | 민준 | Valerie | 밸러리 |\n| Haejun | 해준 | Minju | 민주 | Valkyrie | 발키리 |\n| Haerang | 해랑 | Minjung | 민정 | Vanessa | 바네사 |\n| Hailey | 헤일리 | Minsang | 민상 | Verna | 베르나 |\n| Hajun | 하준 | Minsu | 민수 | Victoria | 빅토리아 |\n| Hakchul | 학철 | Mio | 미오 | Viktor | 빅토르 |\n| Hamchu | 햄쮸 | Miran Choi | 최미란 | Vincent | 빈센트 |\n| Han Taesung Caster | 한태성 캐스터 | Mirine | 미리내 | Vivien | 비비안 |\n| Hana | 하나 | Miseon | 미선 | Wade | 웨이드 |\n| Hangyeol | 한결 | Miso | 미소 | Walter | 월터 |\n| Hanjun | 한준 | Mister Gop | 미스터 갑 | Wangkwon | 왕권 |\n| Hanna | 한나 | Monggun | 몽군 | Weather Reporter Sky | 하늘 캐스터 |\n| Hans | 한스 | Moonjung | 문정 | West | 웨스트 |\n| Hansol | 한솔 | Mooyeol | 무열 | Wonho | 원호 |\n| Hanyoung | 한영 | Morgan | 모건 | Wonkyung | 원경 |\n| Harin | 하린 | Moru | 머루 | Wonwoo | 원우 |\n| Harper | 하퍼 | Mrs. Claus | 미세스 클로스 | Wooju | 우주 |\n| Hayul | 하율 | Munseok | 문석 | Woosung | 우성 |\n| Heejoon | 희준 | Munsu | 문수 | Xavier | 자비에르 |\n| Helena | 헬레나 | Myeonghee | 명희 | Yeeun | 예은 |\n| Henry | 헨리 | Myungil | 명일 | Yejin | 예진 |\n| Homun Sim | 심호문 | Myungjoo | 명주 | Yejoon | 예준 |\n| Hoon | 훈 | Naeun | 나은 | YeLin | 예린 |\n| Hosik | 호식 | Najin | 나진 | Yena | 예나 |\n| Hosun | 호선 | Namjoon | 남준 | Yeonah | 연아 |\n| Hoyoung | 호영 | Nana | 나나 | Yeonggeol | 영걸 |\n| Hugh | 휴 | Nari | 개나리 | Yeonhwa | 연화 |\n| Hwimin | 휘민 | Nathan | 네이선 | Yeonja | 연자 |\n| Hyejung | 혜정 | Neel | 닐 | Yeonsuh | 연서 |\n| Hyelee | 혜리 | Neoguard | 네오가드 | Yeonwoo | 연우 |\n| Hyemin | 혜민 | News anchor Jinsub | 송진섭 앵커 | Yeseul | 예슬 |\n| Hyena | 혜나 | Newscaster John | 존 앵커 | Yongsik | 용식이 |\n| Hyeongjin | 형진 | Nia | 니아 | Yoonseo | 윤서 |\n| Hyera | 혜라 | Noa | 노아 | Yoonsung | 윤성 |\n| Hyesu | 홈쇼핑 혜수 | Noel | 노엘 | Younggil | 영길 |\n| Hyewon | 혜원 | Noeul | 노을 | Younghee | 영희 |\n| Hyun | 현 | Norah | 노라 | Younghwan | 영환 |\n| Hyunji | 현지 | Nova | 노바 | Youngji | 영지 |\n| Hyunjin | 현진 | Old radio | 레디오 | Youngkyu | 영규 |\n| Hyunju | 현주 | Oliver | 올리버 | Youngmok | 영목 |\n| Hyunkyung | 현경 | Olivia | 올리비아 | Yubin | 유빈 |\n| Hyunmin | 현민 | Oscar | 오스카 | Yujin | 유진 |\n| Hyunseung | 현승 | Owen | 오언 | Yumi | 유미 |\n| HyunWoo | 현우 | P-0150N | 포이즌 | Yumin | 유민 |\n| Ian | 이안 | Pacang | 파캉 | Yunbin | 윤빈 |\n| Icarus | 이카루스 | Paige | 페이지 | Yunjeong | 윤정 |\n| Igyeom | 이겸 | Pangpang | 팡팡 | Yura | 유라 |\n| Ijun | 이준 | Patricia | 패트리샤 | Yuri | 유리 |\n| Ilho | 일호 | Patrick | 패트릭 | Yuseong | 유성 |\n| Ina | 이나 | Peter | 피터 | Zoey | 조이 |\n| Inhwa | 인화 | Philip | 필립 | | |\n\n
\n" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "사람이 읽을 수 있는 보이스 이름" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입 — `original` 은 타입캐스트가 기본 제공하는 스톡 보이스, `custom` 은 사용자가 업로드/클론한 보이스입니다." } }, "description": "모델별로 그룹화된 감정과 향상된 메타데이터가 있는 V2 보이스 응답 모델" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대입니다." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별(`male` 또는 `female`)입니다." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "지원하는 TTS 모델과 감정 목록입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유 보이스 식별자입니다. 기본 보이스는 `tc_`, 커스텀 보이스는 `uc_`로 시작합니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "추천 사용 사례 목록입니다." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "`{\"eng\": \"Daejin\", \"kor\": \"대진\"}` 같은 다국어 보이스 이름입니다." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입(`original` 또는 `custom`)입니다." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "제공되는 경우 사용할 수 있는 미리듣기 오디오 URL입니다." } }, "description": "다국어 이름과 미리듣기 URL을 포함한 보이스 정보입니다." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "API 플랜.\n\n사용 가능한 값:\n- **free**: 제한된 크레딧의 무료 등급\n- **lite**: 중간 수준 크레딧의 라이트 플랜\n- **plus**: 높은 크레딧의 플러스 플랜\n- **custom**: 커스텀 엔터프라이즈 플랜" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "음성 합성에 사용할 TTS 모델 버전. 다양한 모델은 다양한 기능과 품질 수준을 제공합니다.\n\n사용 가능한 모델:\n- **ssfm-v30**: 향상된 플로우와 추가 감정 프리셋이 있는 최신 모델(권장)\n- **ssfm-v21**: 검증된 신뢰성과 일관된 품질을 갖춘 안정적인 모델\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS 모델 버전(예: ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "이 모델에서 지원되는 감정 목록" } }, "description": "버전 및 지원되는 감정을 포함한 모델 정보" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "영어 보이스 이름입니다." }, "kor": { "type": "string", "title": "Kor", "description": "한국어 보이스 이름입니다." } }, "description": "ISO 639-3 언어 코드를 키로 사용하는 다국어 보이스 이름입니다." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "보이스 타입 분류.\n\n- `original` — 타입캐스트가 기본 제공하는 스톡 보이스로, 모든 계정에서 사용 가능합니다.\n- `custom` — 사용자가 본인의 샘플을 업로드하거나 클로닝해서 만든 보이스입니다." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "성별 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **male**: 남성 보이스\n- **female**: 여성 보이스\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "음성 합성에 사용 가능한 감정 프리셋. 각 감정은 생성된 음성의 톤, 속도, 표현력에 영향을 줍니다.\n\n**ssfm-v21 지원 감정 (4종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n\n**ssfm-v30 지원 감정 (7종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n- whisper: 부드럽고 조용한 말\n- toneup: 더 높은 톤 강조\n- tonedown: 더 낮은 톤 강조\n\n/v2/voices API 응답을 통해 각 음성에 사용 가능한 감정을 확인하세요.\n" }, "SmartPrompt": { "type": "object", "title": "스마트 프롬프트 (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "TTSRequest의 `text` 필드 이후에 오는 텍스트. 감정 추론을 위한 전방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 전환을 예측하는 데 도움\r\n- 다음 컨텍스트가 없으면 비워 둡니다\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "프롬프트 유형을 식별하는 판별자 필드. 컨텍스트 인식 감정 추론을 위해 \"smart\"로 설정해야 합니다.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "TTSRequest의 `text` 필드 이전에 오는 텍스트. 감정 추론을 위한 후방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 빌드업과 컨텍스트를 이해하는 데 도움\r\n- 이전 컨텍스트가 없으면 비워 둡니다\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "프리셋 프롬프트 (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "프롬프트 유형을 식별하는 판별자 필드. 프리셋 기반 감정 제어를 위해 \"preset\"으로 설정해야 합니다.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "생성된 음성에 적용할 감정 프리셋.\r\n\r\n지원되는 감정: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "생성된 음성의 감정 표현 강도를 제어합니다.\r\n\r\n- 0.0: 완전히 중립적, 감정 색채 없음\r\n- 0.5: 미묘한 감정 힌트\r\n- 1.0: 표준 감정 표현(기본값)\r\n- 1.5: 강한 감정 강조\r\n- 2.0: 최대 강도, 매우 표현력 있음\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "콘텐츠 유형 필터링을 위한 보이스 사용 사례 카테고리. 각 보이스는 특정 콘텐츠 유형에 대한 적합성을 나타내는 하나 이상의 사용 사례로 태그됩니다.\n\n**사용 가능한 사용 사례:**\n- **Announcer**: 공공 발표 및 프레젠테이션\n- **Anime**: 애니메이션 보이스\n- **Audiobook**: 장문 내레이션 및 스토리텔링\n- **Conversational**: 챗봇 및 대화형 AI\n- **Documentary**: 다큐멘터리 내레이션 및 해설\n- **E-learning**: 교육 콘텐츠 및 튜토리얼\n- **Rapper**: 랩 및 음악 퍼포먼스\n- **Game**: 비디오 게임 보이스 및 내레이션\n- **Tiktok/Reels**: SNS 숏츠\n- **News**: 뉴스 방송\n- **Podcast**: 방송 및 팟캐스트 제작\n- **Voicemail**: IVR 시스템 및 음성 비서\n- **Ads**: 광고 및 홍보 콘텐츠\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "문제를 설명하는 오류 메시지" }, "message": { "type": "string", "description": "구조화된 오류에 대한 설명" }, "error_code": { "type": "string", "description": "구조화된 오류를 식별하는 코드" } }, "description": "기존 오류와 유효성 검사 오류는 `detail`을 사용하며, 구조화된 오류는 `error_code`와 `message`를 사용합니다." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "`status`가 `failed`일 때의 실패 사유입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "source": { "type": "string", "title": "Source", "description": "생성 방식(`instant` 또는 `professional`)입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "UTC 기준 생성 시각입니다." } }, "description": "인증된 계정이 소유한 커스텀 보이스입니다." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "추천 관련도 점수. 값이 높을수록 검색어와 더 잘 맞는 후보입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "`tc_` prefix 가 붙은 타입캐스트 보이스 식별자. 텍스트 음성 변환 요청의 `voice_id` 로 사용할 수 있습니다." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "사람이 읽을 수 있는 보이스 이름." } }, "description": "`GET /v1/voices/recommendations` 응답의 보이스 추천 후보. 관련도 순서로 정렬됩니다." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "커스텀 보이스의 현재 생성 또는 학습 상태입니다." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름 (1~30자)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "보이스가 클로닝된 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "`uc_` prefix 가 붙은 커스텀 보이스 식별자. `POST /v1/text-to-speech` 등 `voice_id` 를 받는 엔드포인트에 그대로 사용할 수 있습니다." } }, "description": "`POST /v1/voices/clone` 응답 — 퀵 클로닝으로 생성된 커스텀 보이스 메타데이터." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "세그먼트 구분값입니다. 항상 `pause`입니다." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "초 단위 쉼 길이입니다. 개별 쉼은 최대 10초, 모든 쉼의 합은 최대 60초입니다.", "exclusiveMinimum": 0 } }, "description": "크레딧 차감 없이 삽입되는 무음 구간입니다.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 단어 단위 정렬 구간." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "현재 구독 플랜명" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "사용 제한 정보" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "크레딧 사용 정보" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "구독 정보 응답 모델" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호/공백 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 문자 단위 정렬 구간." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." } }, "description": "퀵 클로닝 또는 프리미엄 클로닝 생성 요청 결과입니다." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "base64 로 인코딩된 오디오 바이트. `audio_format` 확장자로 디코딩해 파일로 저장할 수 있습니다." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "단어 단위 타임스탬프(문장부호 포함). 요청이 `granularity=char` 일 때는 `null`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "문자 단위 타임스탬프(문장부호와 공백 포함). 요청이 `granularity=word` 일 때는 `null`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "`audio` 필드의 오디오 인코딩 포맷 — `wav` 또는 `mp3` (요청의 `output.audio_format` 에 따라 결정)." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "생성된 오디오의 길이(초)." } }, "description": "TTS 생성 + 타임스탬프 정렬 통합 응답." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "오디오 샘플. WAV 또는 MP3, 최대 25MB, 5초 이상 150초 이하.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "보이스 이름 (1~30자)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "클로닝할 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." } }, "description": "퀵 클로닝 요청을 위한 multipart 본문." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "파일", "format": "binary", "description": "25 MiB 이하이면서 길이가 5초 이상 150초 이하인 WAV 또는 MP3 음성 파일 1개입니다.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "상품 내레이터", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." } }, "description": "퀵 클로닝용 multipart 요청 본문입니다." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "커스텀 보이스 이름", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "파일", "description": "학습에 사용할 WAV 또는 MP3 음성 파일입니다. 하나의 파일을 업로드할 수 있습니다.\r\n\r\n**업로드 제한**\r\n\r\n* WAV 또는 MP3 파일 1개\r\n* 파일 크기: 1 GiB 이하\r\n* 길이: 5분 이상 3시간 이하\r\n* 샘플레이트: 16 kHz 이상\r\n\r\n최상의 결과를 위해 다음 조건을 충족하는 오디오를 권장합니다.\r\n\r\n* 생성된 보이스가 재현하기를 원하는 말투와 최대한 가깝게 녹음해 주세요.\r\n* 배경 소음이 없는 조용한 환경에서 녹음해 주세요.\r\n* 한 명의 화자 목소리만 포함해 주세요.\r\n* `language` 필드에 지정한 언어로 녹음해 주세요.\r\n* 오디오 길이가 길수록 생성되는 보이스의 품질이 향상됩니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." }, "language": { "type": "string", "title": "Language", "example": "kor", "description": "`kor`, `eng` 같은 ISO 639-3 언어 코드입니다." } }, "description": "프리미엄 클로닝용 multipart 요청 본문입니다." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "인증을 위한 API 키. 타입캐스트 API 콘솔에서 API 키를 생성할 수 있습니다." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 프리미엄 클로닝 생성 > WAV 또는 MP3 녹음으로 비동기 프리미엄 보이스 학습을 시작합니다. 녹음, 표시 이름, 지원 TTS 모델, ISO 639-3 언어 코드를 `multipart/form-data`로 전송하세요. **오디오 요구 사항** * WAV 또는 MP3 파일 1개 * 파일 크기: 1 GiB 이하 * 길이: 5분 이상 3시간 이하 * 샘플레이트: 16 kHz 이상 성공하면 `status: training`과 함께 `202 Accepted`를 반환합니다. `GET /v1/custom-voices/{voice_id}`를 호출하여 상태가 `completed` 또는 `failed`가 될 때까지 확인하세요. 학습 완료까지는 최대 2시간이 소요되며, 커스텀 보이스 생성이 완료되거나 실패하면 이메일이 발송됩니다. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "프로덕션 서버" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/custom-voices/professional-clone": { "post": { "tags": [ "Custom Voices" ], "x-mint": { "href": "/ko/api-reference/custom-voices/create-professional-clone" }, "summary": "프리미엄 클로닝 생성", "security": [ { "ApiKeyAuth": [] } ], "responses": { "202": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomVoiceCreateResponse" }, "example": { "name": "커스텀 보이스 이름", "model": "ssfm-v30", "status": "training", "voice_id": "uc_6700000000000000000000bb" } } }, "description": "프리미엄 클로닝 학습을 시작했습니다." }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "인증에 실패했습니다." }, "403": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Professional voice slot limit exceeded", "error_code": "PROFESSIONAL_VOICE_SLOT_EXCEEDED" } } }, "description": "프리미엄 클로닝을 사용할 수 없거나 남은 프리미엄 보이스 슬롯이 없습니다." }, "413": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "File size exceeds maximum limit", "error_code": "AUDIO_FILE_TOO_LARGE" } } }, "description": "업로드한 파일의 합산 크기가 설정된 제한을 초과했습니다." }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Requested language is not supported for the selected model", "error_code": "LANGUAGE_NOT_SUPPORTED" } } }, "description": "언어, 폼, 오디오 형식 또는 선택한 모델을 지원하지 않습니다." }, "503": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Server is temporarily busy. Please retry shortly.", "error_code": "SERVER_BUSY" } } }, "description": "프리미엄 클로닝 서버가 혼잡합니다. 잠시 후 다시 시도하세요." } }, "deprecated": false, "description": "WAV 또는 MP3 녹음으로 비동기 프리미엄 보이스 학습을 시작합니다. 녹음, 표시 이름, 지원 TTS 모델, ISO 639-3 언어 코드를 `multipart/form-data`로 전송하세요.\r\n\r\n**오디오 요구 사항**\r\n\r\n* WAV 또는 MP3 파일 1개\r\n* 파일 크기: 1 GiB 이하\r\n* 길이: 5분 이상 3시간 이하\r\n* 샘플레이트: 16 kHz 이상\r\n\r\n성공하면 `status: training`과 함께 `202 Accepted`를 반환합니다. `GET /v1/custom-voices/{voice_id}`를 호출하여 상태가 `completed` 또는 `failed`가 될 때까지 확인하세요. \r\n\r\n학습 완료까지는 최대 2시간이 소요되며, 커스텀 보이스 생성이 완료되거나 실패하면 이메일이 발송됩니다.", "operationId": "create_professional_clone_v1_custom_voices_professional_clone_post", "requestBody": { "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/Body_create_professional_clone_v1_custom_voices_professional_clone_post" } } }, "required": true, "description": "학습 오디오와 보이스 설정을 담은 multipart 폼입니다." }, "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request POST \\\n --url https://api.typecast.ai/v1/custom-voices/professional-clone \\\n --header 'X-API-KEY: ' \\\n --form 'files=@training-audio.wav' \\\n --form 'name=커스텀 보이스 이름' \\\n --form 'model=ssfm-v30' \\\n --form 'language=kor'\n" }, { "lang": "Python", "label": "Python (requests)", "source": "import requests\n\nwith open(\"training-audio.wav\", \"rb\") as audio:\n response = requests.post(\n \"https://api.typecast.ai/v1/custom-voices/professional-clone\",\n headers={\"X-API-KEY\": \"\"},\n files=[(\"files\", audio)],\n data={\"name\": \"브랜드 보이스\", \"model\": \"ssfm-v30\", \"language\": \"kor\"},\n timeout=120,\n )\nresponse.raise_for_status()\nprint(response.json())\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "허용되는 최대 동시 요청 수" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "전체 커스텀 보이스 슬롯 한도 (professional 포함)" }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "사용 제한 정보" }, "Prompt": { "title": "프롬프트 (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "적용할 감정 프리셋.\r\n\r\nssfm-v21 지원 감정: normal, happy, sad, angry\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "example": 1, "description": "감정 표현 강도 제어(0.0~2.0).\r\n\r\n- 0.0: 완전히 중립적\r\n- 1.0: 표준 표현(기본값)\r\n- 2.0: 최대 강도\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "연령대 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **child**: 어린이 보이스(12세 미만)\n- **teenager**: 청소년 보이스(13-19세)\n- **young_adult**: 청년 보이스(20-35세)\n- **middle_age**: 중년 보이스(36-60세)\n- **elder**: 노년 보이스(60세 이상)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "플랜에서 기본으로 제공하는 총 크레딧" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "사용된 크레딧 수" } }, "description": "크레딧 사용 정보" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대 분류(어린이/청소년/청년/중년/노년)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별 분류(남성/여성)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "사용 가능한 감정이 있는 지원되는 TTS 모델 목록(예: [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유한 보이스 식별자. 기본 제공 보이스는 `tc_` prefix (예: `tc_60e5426de8b95f1d3000d7b5`), `POST /v1/voices/clone` 으로 만든 커스텀 보이스는 `uc_` prefix 를 사용합니다. 본인이 소유한 `uc_` 보이스도 `/v2/voices` 응답에 포함됩니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "이 보이스가 적합한 사용 사례 카테고리 목록\n\n## 보이스 이름 참고\n\n`voice_name` 필드는 영문으로만 반환됩니다. 아래 목록에서 한국어 표기를 확인하실 수 있습니다.\n\n
\n보이스 이름 목록 펼치기 (542개)\n\n| 영어 | 한국어 | 영어 | 한국어 | 영어 | 한국어 |\n| --- | --- | --- | --- | --- | --- |\n| Aaron | 에런 | Inhye | 인혜 | Piljae | 필재 |\n| Abigail | 아비게일 | Inseong | 인성 | Pyeonghwa | 평화 |\n| Ae-ran | 애란 | Instructor Han | 한유격 교관 | Rachel | 레이첼 |\n| Ael | 아엘 | Insun | 인선 | Ravi | 라비 |\n| Agatha | 아가사 | Jabbaba | 자바바 | Rayeon | 라연 |\n| Aiden | 에이든 | Jack | 잭 | Rebecca | 레베카 |\n| Alena | 알레나 | Jack-o’-Lantern | 잭오랜턴 | Reporter Bona | 다보나 기자 |\n| Alex | 알렉스 | Jackson | 잭슨 | Reporter Catalina | 카탈리나 기자 |\n| Amber | 엠버 | Jaeho | 재호 | Reporter Jinsub | 송진섭 기자 |\n| Anchor Hwa | 김경화 앵커 | Jaehun | 재훈 | Reporter Kang | 강수정 기자 |\n| Angela | 안젤라 | Jaejun | 재준 | Reporter Lee | 이승주 기자 |\n| Anja | 안냐 | Jaekyung | 재경 | Reporter Parkchan | 박찬 기자 |\n| Annabelle the Ghost | 귀신 애나벨 | Jaesun | 재선 | Rex | 렉스 |\n| Annie | 애니 | JaeYi | 재이 | Riley | 라일리 |\n| April | 에이프릴 | Jaeyoung | 재영 | Risan Ji | 지리산 |\n| Arang | 아랑 | Jain | 자인 | Rita | 리타 |\n| Ari | 아리 | Jake | 제이크 | River | 리버 |\n| Arin | 아린 | Janet | 재닛 | Robert | 로버트 |\n| Athena | 아테나 | Jangho | 장호 | Robo | 로보 |\n| Audrey | 오드리 | Jangwoon | 장운 | Romi | 로미 |\n| Avery | 에이버리 | Jechan | 제찬 | Ron | 론 |\n| Avong | 아봉 | Jeff | 제프 | Roro | 로로 |\n| Azzi | 아찌 | Jenna | 제나 | Rowoon | 로운 |\n| Babilon | 바빌론 | Jennifer | 제니퍼 | Royce | 로이스 |\n| Bell | 벨 | Jeong Choi | 최정 | Rudolph | 루돌프 |\n| Ben | 벤 | Jeongah | 정아 | Ruri | 루리 |\n| Benny | 베니 | JeongJin | 정진 | Rusty | 러스티 |\n| Beri | 베리 | Jeongseob | 정섭 | Ryan | 라이언 |\n| Billie | 빌리 | Jerome | 제롬 | Ryueun | 류은 |\n| Bomi | 이보미 | Jewby | 쥬비 | Sabrina the Witch | 마녀 사브리나 |\n| Bonggyu | 봉규 | Jian | 지안 | Saeron | 새론 |\n| Bongman Kim | 김봉만 | Jicheol | 지철 | Salty Chan-gu | 삐뚤어진 찬구 |\n| Bono | 보노 | Jihee | 지희 | Samantha | 사만다 |\n| Bora | 보라 | Jihoon | 지훈 | Sammy | 새미 |\n| Brad | 브래드 | Jihyun | 지현 | Sangdo | 상도 |\n| Bruce | 브루스 | Jimmy | 지미 | Sanghoon | 상훈 |\n| Buddy | 버디 | Jin | 진 | Sangwoo | 상우 |\n| Bumsoo | 범수 | Jinhan | 진한 | Santa | 산타 |\n| Buttaguy | 빠다가이 | Jinhang Kim | 김진행 | Santa Claus | 산타클로스 |\n| Caitlyn | 케이틀린 | Jinhee | 진희 | Santa Reporter | VJ 싼타 |\n| Callan | 칼란 | Jinhyuk | 진혁 | Sara | 세라 |\n| Camila | 카밀라 | Jinseo | 진서 | Sean | 션 |\n| Captain Bill | 캡틴 빌 | Jinung | 진웅 | SeHee | 세희 |\n| Carl | 칼 | Jinwoo | 진우 | Seheon | 세헌 |\n| Carlos | 카를로스 | Jiwoo | 지우 | Sejin | 세진 |\n| Carol | 캐롤 | Jiyoon | 지윤 | Sena | 세나 |\n| Catherine | 캐서린 | Jiyoung | 지영 | Seohee | 서희 |\n| Chad | 채드 | Jodie | 조디 | Seojin | 서진 |\n| Chaeah | 채아 | Jolly | 졸리 | Seojoon | 서준 |\n| Chan-gu | 찬구 | Jongdae | 종대 | Seok Choi | 최석 |\n| Changbae | 창배 | Jooeun | 주은 | SeokPil | 한석필 |\n| Changhee | 창희 | Joonghyun | 중현 | Seokpyo | 석표 |\n| Changmin | 창민 | Joongsik | 중식 | Seolhwa | 설화 |\n| Changsu | 박창수 | Joonkyu | 준규 | Seonha | 선하 |\n| Chanhyuk | 찬혁 | Joshua | 조슈아 | Seoyeon | 서연 |\n| Charlotte | 샬롯 | Juha | 주하 | Seoyoon | 서윤 |\n| Chase | 체이스 | Juho | 주호 | Seungah | 승아 |\n| Cheolhoon | 철훈 | Jungbong | 정봉 | Seungheon | 승헌 |\n| Cheolyong | 리철용 | Junghee | 정희 | Seungho | 승호 |\n| Cherry | 체리 | Jungjae | 정재 | Seunghwa | 승화 |\n| Chester | 체스터 | Jungmin | 정민 | Seungjae | 승재 |\n| Chiho | 치호 | Jungseok | 정석 | Seungmoon | 승문 |\n| Chloe | 클로이 | Jungsoon | 정순 | Seungwon | 승원 |\n| Choyeon | 강초연 교관 | Jungwon | 정원 | Seungyeon | 승연 |\n| Chungah | 청아 | Junhee | 준희 | Sewoo | 세우 |\n| Chunsik Kang | 강춘식 기자 | Junho | 준호 | Shana | 샤나 |\n| Claire | 클레어 | JunKi | 준기 | Sheriff Kim | 김반장 |\n| Classic Narrator | 미스타 변사 | Junsang | 준상 | Shinhe | 신혜 |\n| Cole | 콜 | Junseong | 준성 | Shinwook | 신욱 |\n| Cox | 콕스 | Justice Roh | 정의로 | Shotgun | 샷건 |\n| Cyrus | 사이러스 | Justin | 저스틴 | Simon | 사이먼 |\n| Dabin | 다빈 | Juwon | 주원 | Sindarin | 신다린 |\n| Daegil | 대길 | Juyoung | 주영 | Sio | 시오 |\n| Dahee | 다희 | K-Santa | 산타 할아버지 | Siwon | 시원 |\n| Dahyeon | 다현 | Kangil | 강일 | Siwoo | 시우 |\n| Damian | 데이미언 | Kanno | 칸노 | Siyeon | 시연 |\n| Dan | 댄 | Katie | 케이티 | Skylar | 스카일러 |\n| Dana | 다나 | Kelly | 켈리 | Slushy | 슬러시 |\n| Dasom | 다솜 | Kelsey | 켈시 | Smoke | 스모크 |\n| David | 데이비드 | Kevin | 케빈 | Sohye | 소혜 |\n| Dean | 딘 | Keybo | 키보 | Sojin | 소진 |\n| Deokhwan | 덕환 | Kijang Kim | 김기장 | Soobin | 수빈 |\n| Dohan | 도한 | Killian the Vampire | 뱀파이어 킬리언 | Sookhee | 숙희 |\n| Dohee | 도희 | Kiseob | 기섭 | Sooni | 순이 |\n| Dohyun | 도현 | Klip Kim | 클립 킴 | Sophia | 소피아 |\n| Dollar Jr. | 달러 주니어 | Koombo | 쿰보 | Sora | 소라 |\n| Doug | 더그 | Kristen | 크리스틴 | Soye | 소예 |\n| Doughnut | 도넛 | Kukhee | 국희 | Soyi | 소이 |\n| DU5T | 더스트 | Kwonil | 권일 | Soyoung | 소영 |\n| Duckchun | 덕춘 | Kyumin | 규민 | Soyul | 소율 |\n| Duke | 듀크 | Kyungho | 경호 | Spice | 스파이스 |\n| Dukgu | 덕구 | Kyungsoo | 경수 | Sportscaster Kang | 강수정 캐스터 |\n| Duman | 두만 | Kyungsook | 경숙 | Sportscaster Tony | 이영광 캐스터 |\n| Dupil | 곽두필 | Lady Cho | 단희빈 | Starling | 스탈링 |\n| DVZY | 데이지 | Lala | 라라 | Stephanie | 스테파니 |\n| Dylan | 딜런 | Lamie | 라미 | Storyteller Jinsub | 송진섭 스토리텔러 |\n| E-seller Sherri | 쇼린이 | Landon | 랜던 | Sua | 수아 |\n| Echo | 에코 | Larry | 래리 | Suho | 수호 |\n| Edward | 에드워드 | Leo | 레오 | Suji | 수지 |\n| Elias | 엘리아스 | Liam | 리암 | Sujin | 수진 |\n| Elise | 엘리스 | Lindsay | 린지 | Sullock Hong | 홍설록 |\n| Ella | 엘라 | Liz | 리즈 | Sumin | 수민 |\n| Eman | 이만 | Lloyd | 로이드 | Sungbae | 성배 |\n| Emma | 엠마 | Logan | 로건 | Sunggyu | 성규 |\n| Eunbin | 은빈 | Lucille | 루실 | Sungho | 성호 |\n| Eunchae | 은채 | Luna | 루나 | Sunghoon | 성훈 |\n| Eunha | 은하 | Lydia | 리디아 | Sunghyun | 성현 |\n| Eunsol | 은솔 | Maddie | 매디 | Sungjun | 홈쇼핑 성준 |\n| Frankenstein | 프랑켄슈타인 | Maisie | 메이시 | Sungkwon | 성권 |\n| Furnando | 터르난도 | Margaret | 마가렛 | Sungtae | 성태 |\n| Gaeul | 가을 | Margot | 마고 | Sungwook | 성욱 |\n| Gahee | 가희 | Matthew | 매튜 | Sunyoung | 선영 |\n| George | 조지 | MBTI EF (F) | MBTI EF 여 | Suyoon | 수윤 |\n| GeumHee | 금희 | MBTI EF (M) | MBTI EF 남 | Sylvia | 실비아 |\n| Geunhyeok | 근혁 | MBTI ET (F) | MBTI ET 여 | Taebaek | 장태백 |\n| Geunseok | 근석 | MBTI ET (M) | MBTI ET 남 | Taeji | 태지 |\n| Geunwoo | 근우 | MBTI IF (F) | MBTI IF 여 | Taejoong | 태중 |\n| Geunyeong | 근영 | MBTI IF (M) | MBTI IF 남 | Taemin | 태민 |\n| Ggami | 까미 | MBTI IT (F) | MBTI IT 여 | Taesub | 태섭 |\n| Ggomi | 꼬미 | MBTI IT (M) | MBTI IT 남 | Taewoo | 태우 |\n| Glenda | 글렌다 | MC Kong | MC콩 | Teal | 틸 |\n| Goat Kim | 김고트 | MC TypeCast | MC 타캐 | Tessa | 테사 |\n| Gongchul | 공철 | Mia | 미아 | Tian | 티안 |\n| Gowoon | 고운 | Michael | 마이클 | Tim | 팀 |\n| Grace | 그레이스 | Mija | 오미자 | Tina | 티나 |\n| Graham | 그레이엄 | Mijin | 미진 | Toby | 토비 |\n| Gunseok | 건석 | Mikyung | 미경 | Tommy | 토미 |\n| Gunwoo | 건우 | Millie | 밀리 | Tyson | 타이슨 |\n| Guri | 구리 | Minchae | 민채 | UiChan | 의찬이 |\n| Gus | 거스 | Minji | 민지 | Uncle Hank | 행크 |\n| Ha Eun | 하은 | Minjoon | 민준 | Valerie | 밸러리 |\n| Haejun | 해준 | Minju | 민주 | Valkyrie | 발키리 |\n| Haerang | 해랑 | Minjung | 민정 | Vanessa | 바네사 |\n| Hailey | 헤일리 | Minsang | 민상 | Verna | 베르나 |\n| Hajun | 하준 | Minsu | 민수 | Victoria | 빅토리아 |\n| Hakchul | 학철 | Mio | 미오 | Viktor | 빅토르 |\n| Hamchu | 햄쮸 | Miran Choi | 최미란 | Vincent | 빈센트 |\n| Han Taesung Caster | 한태성 캐스터 | Mirine | 미리내 | Vivien | 비비안 |\n| Hana | 하나 | Miseon | 미선 | Wade | 웨이드 |\n| Hangyeol | 한결 | Miso | 미소 | Walter | 월터 |\n| Hanjun | 한준 | Mister Gop | 미스터 갑 | Wangkwon | 왕권 |\n| Hanna | 한나 | Monggun | 몽군 | Weather Reporter Sky | 하늘 캐스터 |\n| Hans | 한스 | Moonjung | 문정 | West | 웨스트 |\n| Hansol | 한솔 | Mooyeol | 무열 | Wonho | 원호 |\n| Hanyoung | 한영 | Morgan | 모건 | Wonkyung | 원경 |\n| Harin | 하린 | Moru | 머루 | Wonwoo | 원우 |\n| Harper | 하퍼 | Mrs. Claus | 미세스 클로스 | Wooju | 우주 |\n| Hayul | 하율 | Munseok | 문석 | Woosung | 우성 |\n| Heejoon | 희준 | Munsu | 문수 | Xavier | 자비에르 |\n| Helena | 헬레나 | Myeonghee | 명희 | Yeeun | 예은 |\n| Henry | 헨리 | Myungil | 명일 | Yejin | 예진 |\n| Homun Sim | 심호문 | Myungjoo | 명주 | Yejoon | 예준 |\n| Hoon | 훈 | Naeun | 나은 | YeLin | 예린 |\n| Hosik | 호식 | Najin | 나진 | Yena | 예나 |\n| Hosun | 호선 | Namjoon | 남준 | Yeonah | 연아 |\n| Hoyoung | 호영 | Nana | 나나 | Yeonggeol | 영걸 |\n| Hugh | 휴 | Nari | 개나리 | Yeonhwa | 연화 |\n| Hwimin | 휘민 | Nathan | 네이선 | Yeonja | 연자 |\n| Hyejung | 혜정 | Neel | 닐 | Yeonsuh | 연서 |\n| Hyelee | 혜리 | Neoguard | 네오가드 | Yeonwoo | 연우 |\n| Hyemin | 혜민 | News anchor Jinsub | 송진섭 앵커 | Yeseul | 예슬 |\n| Hyena | 혜나 | Newscaster John | 존 앵커 | Yongsik | 용식이 |\n| Hyeongjin | 형진 | Nia | 니아 | Yoonseo | 윤서 |\n| Hyera | 혜라 | Noa | 노아 | Yoonsung | 윤성 |\n| Hyesu | 홈쇼핑 혜수 | Noel | 노엘 | Younggil | 영길 |\n| Hyewon | 혜원 | Noeul | 노을 | Younghee | 영희 |\n| Hyun | 현 | Norah | 노라 | Younghwan | 영환 |\n| Hyunji | 현지 | Nova | 노바 | Youngji | 영지 |\n| Hyunjin | 현진 | Old radio | 레디오 | Youngkyu | 영규 |\n| Hyunju | 현주 | Oliver | 올리버 | Youngmok | 영목 |\n| Hyunkyung | 현경 | Olivia | 올리비아 | Yubin | 유빈 |\n| Hyunmin | 현민 | Oscar | 오스카 | Yujin | 유진 |\n| Hyunseung | 현승 | Owen | 오언 | Yumi | 유미 |\n| HyunWoo | 현우 | P-0150N | 포이즌 | Yumin | 유민 |\n| Ian | 이안 | Pacang | 파캉 | Yunbin | 윤빈 |\n| Icarus | 이카루스 | Paige | 페이지 | Yunjeong | 윤정 |\n| Igyeom | 이겸 | Pangpang | 팡팡 | Yura | 유라 |\n| Ijun | 이준 | Patricia | 패트리샤 | Yuri | 유리 |\n| Ilho | 일호 | Patrick | 패트릭 | Yuseong | 유성 |\n| Ina | 이나 | Peter | 피터 | Zoey | 조이 |\n| Inhwa | 인화 | Philip | 필립 | | |\n\n
\n" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "사람이 읽을 수 있는 보이스 이름" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입 — `original` 은 타입캐스트가 기본 제공하는 스톡 보이스, `custom` 은 사용자가 업로드/클론한 보이스입니다." } }, "description": "모델별로 그룹화된 감정과 향상된 메타데이터가 있는 V2 보이스 응답 모델" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대입니다." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별(`male` 또는 `female`)입니다." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "지원하는 TTS 모델과 감정 목록입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유 보이스 식별자입니다. 기본 보이스는 `tc_`, 커스텀 보이스는 `uc_`로 시작합니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "추천 사용 사례 목록입니다." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "`{\"eng\": \"Daejin\", \"kor\": \"대진\"}` 같은 다국어 보이스 이름입니다." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입(`original` 또는 `custom`)입니다." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "제공되는 경우 사용할 수 있는 미리듣기 오디오 URL입니다." } }, "description": "다국어 이름과 미리듣기 URL을 포함한 보이스 정보입니다." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "API 플랜.\n\n사용 가능한 값:\n- **free**: 제한된 크레딧의 무료 등급\n- **lite**: 중간 수준 크레딧의 라이트 플랜\n- **plus**: 높은 크레딧의 플러스 플랜\n- **custom**: 커스텀 엔터프라이즈 플랜" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "음성 합성에 사용할 TTS 모델 버전. 다양한 모델은 다양한 기능과 품질 수준을 제공합니다.\n\n사용 가능한 모델:\n- **ssfm-v30**: 향상된 플로우와 추가 감정 프리셋이 있는 최신 모델(권장)\n- **ssfm-v21**: 검증된 신뢰성과 일관된 품질을 갖춘 안정적인 모델\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS 모델 버전(예: ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "이 모델에서 지원되는 감정 목록" } }, "description": "버전 및 지원되는 감정을 포함한 모델 정보" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "영어 보이스 이름입니다." }, "kor": { "type": "string", "title": "Kor", "description": "한국어 보이스 이름입니다." } }, "description": "ISO 639-3 언어 코드를 키로 사용하는 다국어 보이스 이름입니다." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "보이스 타입 분류.\n\n- `original` — 타입캐스트가 기본 제공하는 스톡 보이스로, 모든 계정에서 사용 가능합니다.\n- `custom` — 사용자가 본인의 샘플을 업로드하거나 클로닝해서 만든 보이스입니다." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "성별 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **male**: 남성 보이스\n- **female**: 여성 보이스\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "음성 합성에 사용 가능한 감정 프리셋. 각 감정은 생성된 음성의 톤, 속도, 표현력에 영향을 줍니다.\n\n**ssfm-v21 지원 감정 (4종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n\n**ssfm-v30 지원 감정 (7종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n- whisper: 부드럽고 조용한 말\n- toneup: 더 높은 톤 강조\n- tonedown: 더 낮은 톤 강조\n\n/v2/voices API 응답을 통해 각 음성에 사용 가능한 감정을 확인하세요.\n" }, "SmartPrompt": { "type": "object", "title": "스마트 프롬프트 (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "TTSRequest의 `text` 필드 이후에 오는 텍스트. 감정 추론을 위한 전방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 전환을 예측하는 데 도움\r\n- 다음 컨텍스트가 없으면 비워 둡니다\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "프롬프트 유형을 식별하는 판별자 필드. 컨텍스트 인식 감정 추론을 위해 \"smart\"로 설정해야 합니다.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "TTSRequest의 `text` 필드 이전에 오는 텍스트. 감정 추론을 위한 후방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 빌드업과 컨텍스트를 이해하는 데 도움\r\n- 이전 컨텍스트가 없으면 비워 둡니다\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "프리셋 프롬프트 (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "프롬프트 유형을 식별하는 판별자 필드. 프리셋 기반 감정 제어를 위해 \"preset\"으로 설정해야 합니다.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "생성된 음성에 적용할 감정 프리셋.\r\n\r\n지원되는 감정: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "생성된 음성의 감정 표현 강도를 제어합니다.\r\n\r\n- 0.0: 완전히 중립적, 감정 색채 없음\r\n- 0.5: 미묘한 감정 힌트\r\n- 1.0: 표준 감정 표현(기본값)\r\n- 1.5: 강한 감정 강조\r\n- 2.0: 최대 강도, 매우 표현력 있음\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "콘텐츠 유형 필터링을 위한 보이스 사용 사례 카테고리. 각 보이스는 특정 콘텐츠 유형에 대한 적합성을 나타내는 하나 이상의 사용 사례로 태그됩니다.\n\n**사용 가능한 사용 사례:**\n- **Announcer**: 공공 발표 및 프레젠테이션\n- **Anime**: 애니메이션 보이스\n- **Audiobook**: 장문 내레이션 및 스토리텔링\n- **Conversational**: 챗봇 및 대화형 AI\n- **Documentary**: 다큐멘터리 내레이션 및 해설\n- **E-learning**: 교육 콘텐츠 및 튜토리얼\n- **Rapper**: 랩 및 음악 퍼포먼스\n- **Game**: 비디오 게임 보이스 및 내레이션\n- **Tiktok/Reels**: SNS 숏츠\n- **News**: 뉴스 방송\n- **Podcast**: 방송 및 팟캐스트 제작\n- **Voicemail**: IVR 시스템 및 음성 비서\n- **Ads**: 광고 및 홍보 콘텐츠\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "문제를 설명하는 오류 메시지" }, "message": { "type": "string", "description": "구조화된 오류에 대한 설명" }, "error_code": { "type": "string", "description": "구조화된 오류를 식별하는 코드" } }, "description": "기존 오류와 유효성 검사 오류는 `detail`을 사용하며, 구조화된 오류는 `error_code`와 `message`를 사용합니다." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "`status`가 `failed`일 때의 실패 사유입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "source": { "type": "string", "title": "Source", "description": "생성 방식(`instant` 또는 `professional`)입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "UTC 기준 생성 시각입니다." } }, "description": "인증된 계정이 소유한 커스텀 보이스입니다." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "추천 관련도 점수. 값이 높을수록 검색어와 더 잘 맞는 후보입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "`tc_` prefix 가 붙은 타입캐스트 보이스 식별자. 텍스트 음성 변환 요청의 `voice_id` 로 사용할 수 있습니다." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "사람이 읽을 수 있는 보이스 이름." } }, "description": "`GET /v1/voices/recommendations` 응답의 보이스 추천 후보. 관련도 순서로 정렬됩니다." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "커스텀 보이스의 현재 생성 또는 학습 상태입니다." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름 (1~30자)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "보이스가 클로닝된 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "`uc_` prefix 가 붙은 커스텀 보이스 식별자. `POST /v1/text-to-speech` 등 `voice_id` 를 받는 엔드포인트에 그대로 사용할 수 있습니다." } }, "description": "`POST /v1/voices/clone` 응답 — 퀵 클로닝으로 생성된 커스텀 보이스 메타데이터." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "세그먼트 구분값입니다. 항상 `pause`입니다." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "초 단위 쉼 길이입니다. 개별 쉼은 최대 10초, 모든 쉼의 합은 최대 60초입니다.", "exclusiveMinimum": 0 } }, "description": "크레딧 차감 없이 삽입되는 무음 구간입니다.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 단어 단위 정렬 구간." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "현재 구독 플랜명" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "사용 제한 정보" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "크레딧 사용 정보" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "구독 정보 응답 모델" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호/공백 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 문자 단위 정렬 구간." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." } }, "description": "퀵 클로닝 또는 프리미엄 클로닝 생성 요청 결과입니다." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "base64 로 인코딩된 오디오 바이트. `audio_format` 확장자로 디코딩해 파일로 저장할 수 있습니다." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "단어 단위 타임스탬프(문장부호 포함). 요청이 `granularity=char` 일 때는 `null`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "문자 단위 타임스탬프(문장부호와 공백 포함). 요청이 `granularity=word` 일 때는 `null`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "`audio` 필드의 오디오 인코딩 포맷 — `wav` 또는 `mp3` (요청의 `output.audio_format` 에 따라 결정)." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "생성된 오디오의 길이(초)." } }, "description": "TTS 생성 + 타임스탬프 정렬 통합 응답." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "오디오 샘플. WAV 또는 MP3, 최대 25MB, 5초 이상 150초 이하.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "보이스 이름 (1~30자)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "클로닝할 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." } }, "description": "퀵 클로닝 요청을 위한 multipart 본문." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "파일", "format": "binary", "description": "25 MiB 이하이면서 길이가 5초 이상 150초 이하인 WAV 또는 MP3 음성 파일 1개입니다.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "상품 내레이터", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." } }, "description": "퀵 클로닝용 multipart 요청 본문입니다." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "커스텀 보이스 이름", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "파일", "description": "학습에 사용할 WAV 또는 MP3 음성 파일입니다. 하나의 파일을 업로드할 수 있습니다.\r\n\r\n**업로드 제한**\r\n\r\n* WAV 또는 MP3 파일 1개\r\n* 파일 크기: 1 GiB 이하\r\n* 길이: 5분 이상 3시간 이하\r\n* 샘플레이트: 16 kHz 이상\r\n\r\n최상의 결과를 위해 다음 조건을 충족하는 오디오를 권장합니다.\r\n\r\n* 생성된 보이스가 재현하기를 원하는 말투와 최대한 가깝게 녹음해 주세요.\r\n* 배경 소음이 없는 조용한 환경에서 녹음해 주세요.\r\n* 한 명의 화자 목소리만 포함해 주세요.\r\n* `language` 필드에 지정한 언어로 녹음해 주세요.\r\n* 오디오 길이가 길수록 생성되는 보이스의 품질이 향상됩니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." }, "language": { "type": "string", "title": "Language", "example": "kor", "description": "`kor`, `eng` 같은 ISO 639-3 언어 코드입니다." } }, "description": "프리미엄 클로닝용 multipart 요청 본문입니다." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "인증을 위한 API 키. 타입캐스트 API 콘솔에서 API 키를 생성할 수 있습니다." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 퀵 클로닝 생성 > WAV 또는 MP3 녹음 파일 한 개로 합성 가능한 커스텀 보이스를 생성합니다. 오디오, 표시 이름, 지원 TTS 모델을 `multipart/form-data`로 전송하세요. **오디오 요구 사항** - WAV 또는 MP3 파일 1개 - 파일 크기: 25 MiB 이하 - 길이: 5초 이상 150초 이하 커스텀 보이스 슬롯 한 개를 사용하며 성공하면 생성이 완료된 보이스를 즉시 반환합니다. 지원 모델은 계정 및 출시 상태에 따라 달라질 수 있습니다. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "프로덕션 서버" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/custom-voices/instant-clone": { "post": { "tags": [ "Custom Voices" ], "x-mint": { "href": "/ko/api-reference/custom-voices/create-instant-clone" }, "summary": "퀵 클로닝 생성", "responses": { "201": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomVoiceCreateResponse" }, "example": { "name": "상품 내레이터", "model": "ssfm-v30", "status": "completed", "voice_id": "uc_6700000000000000000000aa" } } }, "description": "퀵 클로닝 보이스를 생성했습니다." }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "인증에 실패했습니다." }, "403": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Custom voice slot limit exceeded", "error_code": "CUSTOM_VOICE_SLOT_EXCEEDED" } } }, "description": "클로닝을 사용할 수 없거나 남은 커스텀 보이스 슬롯이 없습니다." }, "413": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "File size exceeds maximum limit", "error_code": "AUDIO_FILE_TOO_LARGE" } } }, "description": "업로드한 오디오가 설정된 크기 제한을 초과했습니다." }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Requested model is not supported for this voice", "error_code": "VOICE_MODEL_NOT_SUPPORTED" } } }, "description": "폼, 오디오 형식 또는 선택한 모델을 지원하지 않습니다." }, "503": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Server is temporarily busy. Please retry shortly.", "error_code": "SERVER_BUSY" } } }, "description": "클로닝 서버가 혼잡합니다. 잠시 후 다시 시도하세요." } }, "description": "WAV 또는 MP3 녹음 파일 한 개로 합성 가능한 커스텀 보이스를 생성합니다. 오디오, 표시 이름, 지원 TTS 모델을 `multipart/form-data`로 전송하세요.\n\n**오디오 요구 사항**\n- WAV 또는 MP3 파일 1개\n- 파일 크기: 25 MiB 이하\n- 길이: 5초 이상 150초 이하\n\n커스텀 보이스 슬롯 한 개를 사용하며 성공하면 생성이 완료된 보이스를 즉시 반환합니다. 지원 모델은 계정 및 출시 상태에 따라 달라질 수 있습니다.", "operationId": "create_instant_clone_v1_custom_voices_instant_clone_post", "requestBody": { "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/Body_create_instant_clone_v1_custom_voices_instant_clone_post" } } }, "required": true, "description": "원본 녹음과 보이스 설정을 담은 multipart 폼입니다." }, "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request POST \\\n --url https://api.typecast.ai/v1/custom-voices/instant-clone \\\n --header 'X-API-KEY: ' \\\n --form 'file=@voice-sample.wav' \\\n --form 'name=상품 내레이터' \\\n --form 'model=ssfm-v30'\n" }, { "lang": "Python", "label": "Python (requests)", "source": "import requests\n\nwith open(\"voice-sample.wav\", \"rb\") as audio:\n response = requests.post(\n \"https://api.typecast.ai/v1/custom-voices/instant-clone\",\n headers={\"X-API-KEY\": \"\"},\n files={\"file\": audio},\n data={\"name\": \"상품 내레이터\", \"model\": \"ssfm-v30\"},\n timeout=120,\n )\nresponse.raise_for_status()\nprint(response.json())\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "허용되는 최대 동시 요청 수" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "전체 커스텀 보이스 슬롯 한도 (professional 포함)" }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "사용 제한 정보" }, "Prompt": { "title": "프롬프트 (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "적용할 감정 프리셋.\r\n\r\nssfm-v21 지원 감정: normal, happy, sad, angry\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "example": 1, "description": "감정 표현 강도 제어(0.0~2.0).\r\n\r\n- 0.0: 완전히 중립적\r\n- 1.0: 표준 표현(기본값)\r\n- 2.0: 최대 강도\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "연령대 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **child**: 어린이 보이스(12세 미만)\n- **teenager**: 청소년 보이스(13-19세)\n- **young_adult**: 청년 보이스(20-35세)\n- **middle_age**: 중년 보이스(36-60세)\n- **elder**: 노년 보이스(60세 이상)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "플랜에서 기본으로 제공하는 총 크레딧" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "사용된 크레딧 수" } }, "description": "크레딧 사용 정보" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대 분류(어린이/청소년/청년/중년/노년)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별 분류(남성/여성)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "사용 가능한 감정이 있는 지원되는 TTS 모델 목록(예: [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유한 보이스 식별자. 기본 제공 보이스는 `tc_` prefix (예: `tc_60e5426de8b95f1d3000d7b5`), `POST /v1/voices/clone` 으로 만든 커스텀 보이스는 `uc_` prefix 를 사용합니다. 본인이 소유한 `uc_` 보이스도 `/v2/voices` 응답에 포함됩니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "이 보이스가 적합한 사용 사례 카테고리 목록\n\n## 보이스 이름 참고\n\n`voice_name` 필드는 영문으로만 반환됩니다. 아래 목록에서 한국어 표기를 확인하실 수 있습니다.\n\n
\n보이스 이름 목록 펼치기 (542개)\n\n| 영어 | 한국어 | 영어 | 한국어 | 영어 | 한국어 |\n| --- | --- | --- | --- | --- | --- |\n| Aaron | 에런 | Inhye | 인혜 | Piljae | 필재 |\n| Abigail | 아비게일 | Inseong | 인성 | Pyeonghwa | 평화 |\n| Ae-ran | 애란 | Instructor Han | 한유격 교관 | Rachel | 레이첼 |\n| Ael | 아엘 | Insun | 인선 | Ravi | 라비 |\n| Agatha | 아가사 | Jabbaba | 자바바 | Rayeon | 라연 |\n| Aiden | 에이든 | Jack | 잭 | Rebecca | 레베카 |\n| Alena | 알레나 | Jack-o’-Lantern | 잭오랜턴 | Reporter Bona | 다보나 기자 |\n| Alex | 알렉스 | Jackson | 잭슨 | Reporter Catalina | 카탈리나 기자 |\n| Amber | 엠버 | Jaeho | 재호 | Reporter Jinsub | 송진섭 기자 |\n| Anchor Hwa | 김경화 앵커 | Jaehun | 재훈 | Reporter Kang | 강수정 기자 |\n| Angela | 안젤라 | Jaejun | 재준 | Reporter Lee | 이승주 기자 |\n| Anja | 안냐 | Jaekyung | 재경 | Reporter Parkchan | 박찬 기자 |\n| Annabelle the Ghost | 귀신 애나벨 | Jaesun | 재선 | Rex | 렉스 |\n| Annie | 애니 | JaeYi | 재이 | Riley | 라일리 |\n| April | 에이프릴 | Jaeyoung | 재영 | Risan Ji | 지리산 |\n| Arang | 아랑 | Jain | 자인 | Rita | 리타 |\n| Ari | 아리 | Jake | 제이크 | River | 리버 |\n| Arin | 아린 | Janet | 재닛 | Robert | 로버트 |\n| Athena | 아테나 | Jangho | 장호 | Robo | 로보 |\n| Audrey | 오드리 | Jangwoon | 장운 | Romi | 로미 |\n| Avery | 에이버리 | Jechan | 제찬 | Ron | 론 |\n| Avong | 아봉 | Jeff | 제프 | Roro | 로로 |\n| Azzi | 아찌 | Jenna | 제나 | Rowoon | 로운 |\n| Babilon | 바빌론 | Jennifer | 제니퍼 | Royce | 로이스 |\n| Bell | 벨 | Jeong Choi | 최정 | Rudolph | 루돌프 |\n| Ben | 벤 | Jeongah | 정아 | Ruri | 루리 |\n| Benny | 베니 | JeongJin | 정진 | Rusty | 러스티 |\n| Beri | 베리 | Jeongseob | 정섭 | Ryan | 라이언 |\n| Billie | 빌리 | Jerome | 제롬 | Ryueun | 류은 |\n| Bomi | 이보미 | Jewby | 쥬비 | Sabrina the Witch | 마녀 사브리나 |\n| Bonggyu | 봉규 | Jian | 지안 | Saeron | 새론 |\n| Bongman Kim | 김봉만 | Jicheol | 지철 | Salty Chan-gu | 삐뚤어진 찬구 |\n| Bono | 보노 | Jihee | 지희 | Samantha | 사만다 |\n| Bora | 보라 | Jihoon | 지훈 | Sammy | 새미 |\n| Brad | 브래드 | Jihyun | 지현 | Sangdo | 상도 |\n| Bruce | 브루스 | Jimmy | 지미 | Sanghoon | 상훈 |\n| Buddy | 버디 | Jin | 진 | Sangwoo | 상우 |\n| Bumsoo | 범수 | Jinhan | 진한 | Santa | 산타 |\n| Buttaguy | 빠다가이 | Jinhang Kim | 김진행 | Santa Claus | 산타클로스 |\n| Caitlyn | 케이틀린 | Jinhee | 진희 | Santa Reporter | VJ 싼타 |\n| Callan | 칼란 | Jinhyuk | 진혁 | Sara | 세라 |\n| Camila | 카밀라 | Jinseo | 진서 | Sean | 션 |\n| Captain Bill | 캡틴 빌 | Jinung | 진웅 | SeHee | 세희 |\n| Carl | 칼 | Jinwoo | 진우 | Seheon | 세헌 |\n| Carlos | 카를로스 | Jiwoo | 지우 | Sejin | 세진 |\n| Carol | 캐롤 | Jiyoon | 지윤 | Sena | 세나 |\n| Catherine | 캐서린 | Jiyoung | 지영 | Seohee | 서희 |\n| Chad | 채드 | Jodie | 조디 | Seojin | 서진 |\n| Chaeah | 채아 | Jolly | 졸리 | Seojoon | 서준 |\n| Chan-gu | 찬구 | Jongdae | 종대 | Seok Choi | 최석 |\n| Changbae | 창배 | Jooeun | 주은 | SeokPil | 한석필 |\n| Changhee | 창희 | Joonghyun | 중현 | Seokpyo | 석표 |\n| Changmin | 창민 | Joongsik | 중식 | Seolhwa | 설화 |\n| Changsu | 박창수 | Joonkyu | 준규 | Seonha | 선하 |\n| Chanhyuk | 찬혁 | Joshua | 조슈아 | Seoyeon | 서연 |\n| Charlotte | 샬롯 | Juha | 주하 | Seoyoon | 서윤 |\n| Chase | 체이스 | Juho | 주호 | Seungah | 승아 |\n| Cheolhoon | 철훈 | Jungbong | 정봉 | Seungheon | 승헌 |\n| Cheolyong | 리철용 | Junghee | 정희 | Seungho | 승호 |\n| Cherry | 체리 | Jungjae | 정재 | Seunghwa | 승화 |\n| Chester | 체스터 | Jungmin | 정민 | Seungjae | 승재 |\n| Chiho | 치호 | Jungseok | 정석 | Seungmoon | 승문 |\n| Chloe | 클로이 | Jungsoon | 정순 | Seungwon | 승원 |\n| Choyeon | 강초연 교관 | Jungwon | 정원 | Seungyeon | 승연 |\n| Chungah | 청아 | Junhee | 준희 | Sewoo | 세우 |\n| Chunsik Kang | 강춘식 기자 | Junho | 준호 | Shana | 샤나 |\n| Claire | 클레어 | JunKi | 준기 | Sheriff Kim | 김반장 |\n| Classic Narrator | 미스타 변사 | Junsang | 준상 | Shinhe | 신혜 |\n| Cole | 콜 | Junseong | 준성 | Shinwook | 신욱 |\n| Cox | 콕스 | Justice Roh | 정의로 | Shotgun | 샷건 |\n| Cyrus | 사이러스 | Justin | 저스틴 | Simon | 사이먼 |\n| Dabin | 다빈 | Juwon | 주원 | Sindarin | 신다린 |\n| Daegil | 대길 | Juyoung | 주영 | Sio | 시오 |\n| Dahee | 다희 | K-Santa | 산타 할아버지 | Siwon | 시원 |\n| Dahyeon | 다현 | Kangil | 강일 | Siwoo | 시우 |\n| Damian | 데이미언 | Kanno | 칸노 | Siyeon | 시연 |\n| Dan | 댄 | Katie | 케이티 | Skylar | 스카일러 |\n| Dana | 다나 | Kelly | 켈리 | Slushy | 슬러시 |\n| Dasom | 다솜 | Kelsey | 켈시 | Smoke | 스모크 |\n| David | 데이비드 | Kevin | 케빈 | Sohye | 소혜 |\n| Dean | 딘 | Keybo | 키보 | Sojin | 소진 |\n| Deokhwan | 덕환 | Kijang Kim | 김기장 | Soobin | 수빈 |\n| Dohan | 도한 | Killian the Vampire | 뱀파이어 킬리언 | Sookhee | 숙희 |\n| Dohee | 도희 | Kiseob | 기섭 | Sooni | 순이 |\n| Dohyun | 도현 | Klip Kim | 클립 킴 | Sophia | 소피아 |\n| Dollar Jr. | 달러 주니어 | Koombo | 쿰보 | Sora | 소라 |\n| Doug | 더그 | Kristen | 크리스틴 | Soye | 소예 |\n| Doughnut | 도넛 | Kukhee | 국희 | Soyi | 소이 |\n| DU5T | 더스트 | Kwonil | 권일 | Soyoung | 소영 |\n| Duckchun | 덕춘 | Kyumin | 규민 | Soyul | 소율 |\n| Duke | 듀크 | Kyungho | 경호 | Spice | 스파이스 |\n| Dukgu | 덕구 | Kyungsoo | 경수 | Sportscaster Kang | 강수정 캐스터 |\n| Duman | 두만 | Kyungsook | 경숙 | Sportscaster Tony | 이영광 캐스터 |\n| Dupil | 곽두필 | Lady Cho | 단희빈 | Starling | 스탈링 |\n| DVZY | 데이지 | Lala | 라라 | Stephanie | 스테파니 |\n| Dylan | 딜런 | Lamie | 라미 | Storyteller Jinsub | 송진섭 스토리텔러 |\n| E-seller Sherri | 쇼린이 | Landon | 랜던 | Sua | 수아 |\n| Echo | 에코 | Larry | 래리 | Suho | 수호 |\n| Edward | 에드워드 | Leo | 레오 | Suji | 수지 |\n| Elias | 엘리아스 | Liam | 리암 | Sujin | 수진 |\n| Elise | 엘리스 | Lindsay | 린지 | Sullock Hong | 홍설록 |\n| Ella | 엘라 | Liz | 리즈 | Sumin | 수민 |\n| Eman | 이만 | Lloyd | 로이드 | Sungbae | 성배 |\n| Emma | 엠마 | Logan | 로건 | Sunggyu | 성규 |\n| Eunbin | 은빈 | Lucille | 루실 | Sungho | 성호 |\n| Eunchae | 은채 | Luna | 루나 | Sunghoon | 성훈 |\n| Eunha | 은하 | Lydia | 리디아 | Sunghyun | 성현 |\n| Eunsol | 은솔 | Maddie | 매디 | Sungjun | 홈쇼핑 성준 |\n| Frankenstein | 프랑켄슈타인 | Maisie | 메이시 | Sungkwon | 성권 |\n| Furnando | 터르난도 | Margaret | 마가렛 | Sungtae | 성태 |\n| Gaeul | 가을 | Margot | 마고 | Sungwook | 성욱 |\n| Gahee | 가희 | Matthew | 매튜 | Sunyoung | 선영 |\n| George | 조지 | MBTI EF (F) | MBTI EF 여 | Suyoon | 수윤 |\n| GeumHee | 금희 | MBTI EF (M) | MBTI EF 남 | Sylvia | 실비아 |\n| Geunhyeok | 근혁 | MBTI ET (F) | MBTI ET 여 | Taebaek | 장태백 |\n| Geunseok | 근석 | MBTI ET (M) | MBTI ET 남 | Taeji | 태지 |\n| Geunwoo | 근우 | MBTI IF (F) | MBTI IF 여 | Taejoong | 태중 |\n| Geunyeong | 근영 | MBTI IF (M) | MBTI IF 남 | Taemin | 태민 |\n| Ggami | 까미 | MBTI IT (F) | MBTI IT 여 | Taesub | 태섭 |\n| Ggomi | 꼬미 | MBTI IT (M) | MBTI IT 남 | Taewoo | 태우 |\n| Glenda | 글렌다 | MC Kong | MC콩 | Teal | 틸 |\n| Goat Kim | 김고트 | MC TypeCast | MC 타캐 | Tessa | 테사 |\n| Gongchul | 공철 | Mia | 미아 | Tian | 티안 |\n| Gowoon | 고운 | Michael | 마이클 | Tim | 팀 |\n| Grace | 그레이스 | Mija | 오미자 | Tina | 티나 |\n| Graham | 그레이엄 | Mijin | 미진 | Toby | 토비 |\n| Gunseok | 건석 | Mikyung | 미경 | Tommy | 토미 |\n| Gunwoo | 건우 | Millie | 밀리 | Tyson | 타이슨 |\n| Guri | 구리 | Minchae | 민채 | UiChan | 의찬이 |\n| Gus | 거스 | Minji | 민지 | Uncle Hank | 행크 |\n| Ha Eun | 하은 | Minjoon | 민준 | Valerie | 밸러리 |\n| Haejun | 해준 | Minju | 민주 | Valkyrie | 발키리 |\n| Haerang | 해랑 | Minjung | 민정 | Vanessa | 바네사 |\n| Hailey | 헤일리 | Minsang | 민상 | Verna | 베르나 |\n| Hajun | 하준 | Minsu | 민수 | Victoria | 빅토리아 |\n| Hakchul | 학철 | Mio | 미오 | Viktor | 빅토르 |\n| Hamchu | 햄쮸 | Miran Choi | 최미란 | Vincent | 빈센트 |\n| Han Taesung Caster | 한태성 캐스터 | Mirine | 미리내 | Vivien | 비비안 |\n| Hana | 하나 | Miseon | 미선 | Wade | 웨이드 |\n| Hangyeol | 한결 | Miso | 미소 | Walter | 월터 |\n| Hanjun | 한준 | Mister Gop | 미스터 갑 | Wangkwon | 왕권 |\n| Hanna | 한나 | Monggun | 몽군 | Weather Reporter Sky | 하늘 캐스터 |\n| Hans | 한스 | Moonjung | 문정 | West | 웨스트 |\n| Hansol | 한솔 | Mooyeol | 무열 | Wonho | 원호 |\n| Hanyoung | 한영 | Morgan | 모건 | Wonkyung | 원경 |\n| Harin | 하린 | Moru | 머루 | Wonwoo | 원우 |\n| Harper | 하퍼 | Mrs. Claus | 미세스 클로스 | Wooju | 우주 |\n| Hayul | 하율 | Munseok | 문석 | Woosung | 우성 |\n| Heejoon | 희준 | Munsu | 문수 | Xavier | 자비에르 |\n| Helena | 헬레나 | Myeonghee | 명희 | Yeeun | 예은 |\n| Henry | 헨리 | Myungil | 명일 | Yejin | 예진 |\n| Homun Sim | 심호문 | Myungjoo | 명주 | Yejoon | 예준 |\n| Hoon | 훈 | Naeun | 나은 | YeLin | 예린 |\n| Hosik | 호식 | Najin | 나진 | Yena | 예나 |\n| Hosun | 호선 | Namjoon | 남준 | Yeonah | 연아 |\n| Hoyoung | 호영 | Nana | 나나 | Yeonggeol | 영걸 |\n| Hugh | 휴 | Nari | 개나리 | Yeonhwa | 연화 |\n| Hwimin | 휘민 | Nathan | 네이선 | Yeonja | 연자 |\n| Hyejung | 혜정 | Neel | 닐 | Yeonsuh | 연서 |\n| Hyelee | 혜리 | Neoguard | 네오가드 | Yeonwoo | 연우 |\n| Hyemin | 혜민 | News anchor Jinsub | 송진섭 앵커 | Yeseul | 예슬 |\n| Hyena | 혜나 | Newscaster John | 존 앵커 | Yongsik | 용식이 |\n| Hyeongjin | 형진 | Nia | 니아 | Yoonseo | 윤서 |\n| Hyera | 혜라 | Noa | 노아 | Yoonsung | 윤성 |\n| Hyesu | 홈쇼핑 혜수 | Noel | 노엘 | Younggil | 영길 |\n| Hyewon | 혜원 | Noeul | 노을 | Younghee | 영희 |\n| Hyun | 현 | Norah | 노라 | Younghwan | 영환 |\n| Hyunji | 현지 | Nova | 노바 | Youngji | 영지 |\n| Hyunjin | 현진 | Old radio | 레디오 | Youngkyu | 영규 |\n| Hyunju | 현주 | Oliver | 올리버 | Youngmok | 영목 |\n| Hyunkyung | 현경 | Olivia | 올리비아 | Yubin | 유빈 |\n| Hyunmin | 현민 | Oscar | 오스카 | Yujin | 유진 |\n| Hyunseung | 현승 | Owen | 오언 | Yumi | 유미 |\n| HyunWoo | 현우 | P-0150N | 포이즌 | Yumin | 유민 |\n| Ian | 이안 | Pacang | 파캉 | Yunbin | 윤빈 |\n| Icarus | 이카루스 | Paige | 페이지 | Yunjeong | 윤정 |\n| Igyeom | 이겸 | Pangpang | 팡팡 | Yura | 유라 |\n| Ijun | 이준 | Patricia | 패트리샤 | Yuri | 유리 |\n| Ilho | 일호 | Patrick | 패트릭 | Yuseong | 유성 |\n| Ina | 이나 | Peter | 피터 | Zoey | 조이 |\n| Inhwa | 인화 | Philip | 필립 | | |\n\n
\n" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "사람이 읽을 수 있는 보이스 이름" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입 — `original` 은 타입캐스트가 기본 제공하는 스톡 보이스, `custom` 은 사용자가 업로드/클론한 보이스입니다." } }, "description": "모델별로 그룹화된 감정과 향상된 메타데이터가 있는 V2 보이스 응답 모델" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대입니다." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별(`male` 또는 `female`)입니다." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "지원하는 TTS 모델과 감정 목록입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유 보이스 식별자입니다. 기본 보이스는 `tc_`, 커스텀 보이스는 `uc_`로 시작합니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "추천 사용 사례 목록입니다." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "`{\"eng\": \"Daejin\", \"kor\": \"대진\"}` 같은 다국어 보이스 이름입니다." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입(`original` 또는 `custom`)입니다." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "제공되는 경우 사용할 수 있는 미리듣기 오디오 URL입니다." } }, "description": "다국어 이름과 미리듣기 URL을 포함한 보이스 정보입니다." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "API 플랜.\n\n사용 가능한 값:\n- **free**: 제한된 크레딧의 무료 등급\n- **lite**: 중간 수준 크레딧의 라이트 플랜\n- **plus**: 높은 크레딧의 플러스 플랜\n- **custom**: 커스텀 엔터프라이즈 플랜" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "음성 합성에 사용할 TTS 모델 버전. 다양한 모델은 다양한 기능과 품질 수준을 제공합니다.\n\n사용 가능한 모델:\n- **ssfm-v30**: 향상된 플로우와 추가 감정 프리셋이 있는 최신 모델(권장)\n- **ssfm-v21**: 검증된 신뢰성과 일관된 품질을 갖춘 안정적인 모델\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS 모델 버전(예: ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "이 모델에서 지원되는 감정 목록" } }, "description": "버전 및 지원되는 감정을 포함한 모델 정보" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "영어 보이스 이름입니다." }, "kor": { "type": "string", "title": "Kor", "description": "한국어 보이스 이름입니다." } }, "description": "ISO 639-3 언어 코드를 키로 사용하는 다국어 보이스 이름입니다." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "보이스 타입 분류.\n\n- `original` — 타입캐스트가 기본 제공하는 스톡 보이스로, 모든 계정에서 사용 가능합니다.\n- `custom` — 사용자가 본인의 샘플을 업로드하거나 클로닝해서 만든 보이스입니다." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "성별 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **male**: 남성 보이스\n- **female**: 여성 보이스\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "음성 합성에 사용 가능한 감정 프리셋. 각 감정은 생성된 음성의 톤, 속도, 표현력에 영향을 줍니다.\n\n**ssfm-v21 지원 감정 (4종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n\n**ssfm-v30 지원 감정 (7종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n- whisper: 부드럽고 조용한 말\n- toneup: 더 높은 톤 강조\n- tonedown: 더 낮은 톤 강조\n\n/v2/voices API 응답을 통해 각 음성에 사용 가능한 감정을 확인하세요.\n" }, "SmartPrompt": { "type": "object", "title": "스마트 프롬프트 (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "TTSRequest의 `text` 필드 이후에 오는 텍스트. 감정 추론을 위한 전방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 전환을 예측하는 데 도움\r\n- 다음 컨텍스트가 없으면 비워 둡니다\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "프롬프트 유형을 식별하는 판별자 필드. 컨텍스트 인식 감정 추론을 위해 \"smart\"로 설정해야 합니다.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "TTSRequest의 `text` 필드 이전에 오는 텍스트. 감정 추론을 위한 후방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 빌드업과 컨텍스트를 이해하는 데 도움\r\n- 이전 컨텍스트가 없으면 비워 둡니다\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "프리셋 프롬프트 (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "프롬프트 유형을 식별하는 판별자 필드. 프리셋 기반 감정 제어를 위해 \"preset\"으로 설정해야 합니다.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "생성된 음성에 적용할 감정 프리셋.\r\n\r\n지원되는 감정: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "생성된 음성의 감정 표현 강도를 제어합니다.\r\n\r\n- 0.0: 완전히 중립적, 감정 색채 없음\r\n- 0.5: 미묘한 감정 힌트\r\n- 1.0: 표준 감정 표현(기본값)\r\n- 1.5: 강한 감정 강조\r\n- 2.0: 최대 강도, 매우 표현력 있음\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "콘텐츠 유형 필터링을 위한 보이스 사용 사례 카테고리. 각 보이스는 특정 콘텐츠 유형에 대한 적합성을 나타내는 하나 이상의 사용 사례로 태그됩니다.\n\n**사용 가능한 사용 사례:**\n- **Announcer**: 공공 발표 및 프레젠테이션\n- **Anime**: 애니메이션 보이스\n- **Audiobook**: 장문 내레이션 및 스토리텔링\n- **Conversational**: 챗봇 및 대화형 AI\n- **Documentary**: 다큐멘터리 내레이션 및 해설\n- **E-learning**: 교육 콘텐츠 및 튜토리얼\n- **Rapper**: 랩 및 음악 퍼포먼스\n- **Game**: 비디오 게임 보이스 및 내레이션\n- **Tiktok/Reels**: SNS 숏츠\n- **News**: 뉴스 방송\n- **Podcast**: 방송 및 팟캐스트 제작\n- **Voicemail**: IVR 시스템 및 음성 비서\n- **Ads**: 광고 및 홍보 콘텐츠\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "문제를 설명하는 오류 메시지" }, "message": { "type": "string", "description": "구조화된 오류에 대한 설명" }, "error_code": { "type": "string", "description": "구조화된 오류를 식별하는 코드" } }, "description": "기존 오류와 유효성 검사 오류는 `detail`을 사용하며, 구조화된 오류는 `error_code`와 `message`를 사용합니다." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "`status`가 `failed`일 때의 실패 사유입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "source": { "type": "string", "title": "Source", "description": "생성 방식(`instant` 또는 `professional`)입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "UTC 기준 생성 시각입니다." } }, "description": "인증된 계정이 소유한 커스텀 보이스입니다." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "추천 관련도 점수. 값이 높을수록 검색어와 더 잘 맞는 후보입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "`tc_` prefix 가 붙은 타입캐스트 보이스 식별자. 텍스트 음성 변환 요청의 `voice_id` 로 사용할 수 있습니다." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "사람이 읽을 수 있는 보이스 이름." } }, "description": "`GET /v1/voices/recommendations` 응답의 보이스 추천 후보. 관련도 순서로 정렬됩니다." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "커스텀 보이스의 현재 생성 또는 학습 상태입니다." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름 (1~30자)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "보이스가 클로닝된 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "`uc_` prefix 가 붙은 커스텀 보이스 식별자. `POST /v1/text-to-speech` 등 `voice_id` 를 받는 엔드포인트에 그대로 사용할 수 있습니다." } }, "description": "`POST /v1/voices/clone` 응답 — 퀵 클로닝으로 생성된 커스텀 보이스 메타데이터." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "세그먼트 구분값입니다. 항상 `pause`입니다." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "초 단위 쉼 길이입니다. 개별 쉼은 최대 10초, 모든 쉼의 합은 최대 60초입니다.", "exclusiveMinimum": 0 } }, "description": "크레딧 차감 없이 삽입되는 무음 구간입니다.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 단어 단위 정렬 구간." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "현재 구독 플랜명" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "사용 제한 정보" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "크레딧 사용 정보" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "구독 정보 응답 모델" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호/공백 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 문자 단위 정렬 구간." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." } }, "description": "퀵 클로닝 또는 프리미엄 클로닝 생성 요청 결과입니다." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "base64 로 인코딩된 오디오 바이트. `audio_format` 확장자로 디코딩해 파일로 저장할 수 있습니다." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "단어 단위 타임스탬프(문장부호 포함). 요청이 `granularity=char` 일 때는 `null`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "문자 단위 타임스탬프(문장부호와 공백 포함). 요청이 `granularity=word` 일 때는 `null`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "`audio` 필드의 오디오 인코딩 포맷 — `wav` 또는 `mp3` (요청의 `output.audio_format` 에 따라 결정)." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "생성된 오디오의 길이(초)." } }, "description": "TTS 생성 + 타임스탬프 정렬 통합 응답." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "오디오 샘플. WAV 또는 MP3, 최대 25MB, 5초 이상 150초 이하.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "보이스 이름 (1~30자)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "클로닝할 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." } }, "description": "퀵 클로닝 요청을 위한 multipart 본문." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "파일", "format": "binary", "description": "25 MiB 이하이면서 길이가 5초 이상 150초 이하인 WAV 또는 MP3 음성 파일 1개입니다.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "상품 내레이터", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." } }, "description": "퀵 클로닝용 multipart 요청 본문입니다." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "커스텀 보이스 이름", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "파일", "description": "학습에 사용할 WAV 또는 MP3 음성 파일입니다. 하나의 파일을 업로드할 수 있습니다.\r\n\r\n**업로드 제한**\r\n\r\n* WAV 또는 MP3 파일 1개\r\n* 파일 크기: 1 GiB 이하\r\n* 길이: 5분 이상 3시간 이하\r\n* 샘플레이트: 16 kHz 이상\r\n\r\n최상의 결과를 위해 다음 조건을 충족하는 오디오를 권장합니다.\r\n\r\n* 생성된 보이스가 재현하기를 원하는 말투와 최대한 가깝게 녹음해 주세요.\r\n* 배경 소음이 없는 조용한 환경에서 녹음해 주세요.\r\n* 한 명의 화자 목소리만 포함해 주세요.\r\n* `language` 필드에 지정한 언어로 녹음해 주세요.\r\n* 오디오 길이가 길수록 생성되는 보이스의 품질이 향상됩니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." }, "language": { "type": "string", "title": "Language", "example": "kor", "description": "`kor`, `eng` 같은 ISO 639-3 언어 코드입니다." } }, "description": "프리미엄 클로닝용 multipart 요청 본문입니다." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "인증을 위한 API 키. 타입캐스트 API 콘솔에서 API 키를 생성할 수 있습니다." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 커스텀 보이스 목록 조회 > 인증된 계정이 소유한 삭제되지 않은 커스텀 보이스를 조회합니다. 퀵 클로닝과 프리미엄 클로닝 보이스가 모두 포함되며 `pending`, `training`, `completed`, `failed` 상태가 표시될 수 있습니다. 합성 가능 여부는 `status`로 판단하세요. `failed`인 경우에만 `error`에 안전하게 가공된 실패 사유가 포함되며, 나머지 상태에서는 `null`입니다. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "프로덕션 서버" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/custom-voices": { "get": { "tags": [ "Custom Voices" ], "x-mint": { "href": "/ko/api-reference/custom-voices/list-custom-voices" }, "summary": "커스텀 보이스 목록 조회", "responses": { "200": { "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/CustomVoiceItem" }, "title": "Response List Custom Voices V1 Custom Voices Get" }, "example": [ { "name": "상품 내레이터", "error": null, "model": "ssfm-v30", "source": "instant", "status": "completed", "voice_id": "uc_6700000000000000000000aa", "created_at": "2026-08-26T04:15:00Z" }, { "name": "브랜드 보이스", "error": null, "model": "ssfm-v30", "source": "professional", "status": "training", "voice_id": "uc_6700000000000000000000bb", "created_at": "2026-08-26T04:20:00Z" } ] } }, "description": "커스텀 보이스 목록을 조회했습니다." }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "인증에 실패했습니다." } }, "description": "인증된 계정이 소유한 삭제되지 않은 커스텀 보이스를 조회합니다. 퀵 클로닝과 프리미엄 클로닝 보이스가 모두 포함되며 `pending`, `training`, `completed`, `failed` 상태가 표시될 수 있습니다.\n\n합성 가능 여부는 `status`로 판단하세요. `failed`인 경우에만 `error`에 안전하게 가공된 실패 사유가 포함되며, 나머지 상태에서는 `null`입니다.", "operationId": "list_custom_voices_v1_custom_voices_get", "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request GET \\\n --url https://api.typecast.ai/v1/custom-voices \\\n --header 'X-API-KEY: '\n" }, { "lang": "Python", "label": "Python (requests)", "source": "import requests\n\nresponse = requests.get(\n \"https://api.typecast.ai/v1/custom-voices\",\n headers={\"X-API-KEY\": \"\"},\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "허용되는 최대 동시 요청 수" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "전체 커스텀 보이스 슬롯 한도 (professional 포함)" }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "사용 제한 정보" }, "Prompt": { "title": "프롬프트 (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "적용할 감정 프리셋.\r\n\r\nssfm-v21 지원 감정: normal, happy, sad, angry\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "example": 1, "description": "감정 표현 강도 제어(0.0~2.0).\r\n\r\n- 0.0: 완전히 중립적\r\n- 1.0: 표준 표현(기본값)\r\n- 2.0: 최대 강도\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "연령대 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **child**: 어린이 보이스(12세 미만)\n- **teenager**: 청소년 보이스(13-19세)\n- **young_adult**: 청년 보이스(20-35세)\n- **middle_age**: 중년 보이스(36-60세)\n- **elder**: 노년 보이스(60세 이상)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "플랜에서 기본으로 제공하는 총 크레딧" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "사용된 크레딧 수" } }, "description": "크레딧 사용 정보" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대 분류(어린이/청소년/청년/중년/노년)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별 분류(남성/여성)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "사용 가능한 감정이 있는 지원되는 TTS 모델 목록(예: [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유한 보이스 식별자. 기본 제공 보이스는 `tc_` prefix (예: `tc_60e5426de8b95f1d3000d7b5`), `POST /v1/voices/clone` 으로 만든 커스텀 보이스는 `uc_` prefix 를 사용합니다. 본인이 소유한 `uc_` 보이스도 `/v2/voices` 응답에 포함됩니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "이 보이스가 적합한 사용 사례 카테고리 목록\n\n## 보이스 이름 참고\n\n`voice_name` 필드는 영문으로만 반환됩니다. 아래 목록에서 한국어 표기를 확인하실 수 있습니다.\n\n
\n보이스 이름 목록 펼치기 (542개)\n\n| 영어 | 한국어 | 영어 | 한국어 | 영어 | 한국어 |\n| --- | --- | --- | --- | --- | --- |\n| Aaron | 에런 | Inhye | 인혜 | Piljae | 필재 |\n| Abigail | 아비게일 | Inseong | 인성 | Pyeonghwa | 평화 |\n| Ae-ran | 애란 | Instructor Han | 한유격 교관 | Rachel | 레이첼 |\n| Ael | 아엘 | Insun | 인선 | Ravi | 라비 |\n| Agatha | 아가사 | Jabbaba | 자바바 | Rayeon | 라연 |\n| Aiden | 에이든 | Jack | 잭 | Rebecca | 레베카 |\n| Alena | 알레나 | Jack-o’-Lantern | 잭오랜턴 | Reporter Bona | 다보나 기자 |\n| Alex | 알렉스 | Jackson | 잭슨 | Reporter Catalina | 카탈리나 기자 |\n| Amber | 엠버 | Jaeho | 재호 | Reporter Jinsub | 송진섭 기자 |\n| Anchor Hwa | 김경화 앵커 | Jaehun | 재훈 | Reporter Kang | 강수정 기자 |\n| Angela | 안젤라 | Jaejun | 재준 | Reporter Lee | 이승주 기자 |\n| Anja | 안냐 | Jaekyung | 재경 | Reporter Parkchan | 박찬 기자 |\n| Annabelle the Ghost | 귀신 애나벨 | Jaesun | 재선 | Rex | 렉스 |\n| Annie | 애니 | JaeYi | 재이 | Riley | 라일리 |\n| April | 에이프릴 | Jaeyoung | 재영 | Risan Ji | 지리산 |\n| Arang | 아랑 | Jain | 자인 | Rita | 리타 |\n| Ari | 아리 | Jake | 제이크 | River | 리버 |\n| Arin | 아린 | Janet | 재닛 | Robert | 로버트 |\n| Athena | 아테나 | Jangho | 장호 | Robo | 로보 |\n| Audrey | 오드리 | Jangwoon | 장운 | Romi | 로미 |\n| Avery | 에이버리 | Jechan | 제찬 | Ron | 론 |\n| Avong | 아봉 | Jeff | 제프 | Roro | 로로 |\n| Azzi | 아찌 | Jenna | 제나 | Rowoon | 로운 |\n| Babilon | 바빌론 | Jennifer | 제니퍼 | Royce | 로이스 |\n| Bell | 벨 | Jeong Choi | 최정 | Rudolph | 루돌프 |\n| Ben | 벤 | Jeongah | 정아 | Ruri | 루리 |\n| Benny | 베니 | JeongJin | 정진 | Rusty | 러스티 |\n| Beri | 베리 | Jeongseob | 정섭 | Ryan | 라이언 |\n| Billie | 빌리 | Jerome | 제롬 | Ryueun | 류은 |\n| Bomi | 이보미 | Jewby | 쥬비 | Sabrina the Witch | 마녀 사브리나 |\n| Bonggyu | 봉규 | Jian | 지안 | Saeron | 새론 |\n| Bongman Kim | 김봉만 | Jicheol | 지철 | Salty Chan-gu | 삐뚤어진 찬구 |\n| Bono | 보노 | Jihee | 지희 | Samantha | 사만다 |\n| Bora | 보라 | Jihoon | 지훈 | Sammy | 새미 |\n| Brad | 브래드 | Jihyun | 지현 | Sangdo | 상도 |\n| Bruce | 브루스 | Jimmy | 지미 | Sanghoon | 상훈 |\n| Buddy | 버디 | Jin | 진 | Sangwoo | 상우 |\n| Bumsoo | 범수 | Jinhan | 진한 | Santa | 산타 |\n| Buttaguy | 빠다가이 | Jinhang Kim | 김진행 | Santa Claus | 산타클로스 |\n| Caitlyn | 케이틀린 | Jinhee | 진희 | Santa Reporter | VJ 싼타 |\n| Callan | 칼란 | Jinhyuk | 진혁 | Sara | 세라 |\n| Camila | 카밀라 | Jinseo | 진서 | Sean | 션 |\n| Captain Bill | 캡틴 빌 | Jinung | 진웅 | SeHee | 세희 |\n| Carl | 칼 | Jinwoo | 진우 | Seheon | 세헌 |\n| Carlos | 카를로스 | Jiwoo | 지우 | Sejin | 세진 |\n| Carol | 캐롤 | Jiyoon | 지윤 | Sena | 세나 |\n| Catherine | 캐서린 | Jiyoung | 지영 | Seohee | 서희 |\n| Chad | 채드 | Jodie | 조디 | Seojin | 서진 |\n| Chaeah | 채아 | Jolly | 졸리 | Seojoon | 서준 |\n| Chan-gu | 찬구 | Jongdae | 종대 | Seok Choi | 최석 |\n| Changbae | 창배 | Jooeun | 주은 | SeokPil | 한석필 |\n| Changhee | 창희 | Joonghyun | 중현 | Seokpyo | 석표 |\n| Changmin | 창민 | Joongsik | 중식 | Seolhwa | 설화 |\n| Changsu | 박창수 | Joonkyu | 준규 | Seonha | 선하 |\n| Chanhyuk | 찬혁 | Joshua | 조슈아 | Seoyeon | 서연 |\n| Charlotte | 샬롯 | Juha | 주하 | Seoyoon | 서윤 |\n| Chase | 체이스 | Juho | 주호 | Seungah | 승아 |\n| Cheolhoon | 철훈 | Jungbong | 정봉 | Seungheon | 승헌 |\n| Cheolyong | 리철용 | Junghee | 정희 | Seungho | 승호 |\n| Cherry | 체리 | Jungjae | 정재 | Seunghwa | 승화 |\n| Chester | 체스터 | Jungmin | 정민 | Seungjae | 승재 |\n| Chiho | 치호 | Jungseok | 정석 | Seungmoon | 승문 |\n| Chloe | 클로이 | Jungsoon | 정순 | Seungwon | 승원 |\n| Choyeon | 강초연 교관 | Jungwon | 정원 | Seungyeon | 승연 |\n| Chungah | 청아 | Junhee | 준희 | Sewoo | 세우 |\n| Chunsik Kang | 강춘식 기자 | Junho | 준호 | Shana | 샤나 |\n| Claire | 클레어 | JunKi | 준기 | Sheriff Kim | 김반장 |\n| Classic Narrator | 미스타 변사 | Junsang | 준상 | Shinhe | 신혜 |\n| Cole | 콜 | Junseong | 준성 | Shinwook | 신욱 |\n| Cox | 콕스 | Justice Roh | 정의로 | Shotgun | 샷건 |\n| Cyrus | 사이러스 | Justin | 저스틴 | Simon | 사이먼 |\n| Dabin | 다빈 | Juwon | 주원 | Sindarin | 신다린 |\n| Daegil | 대길 | Juyoung | 주영 | Sio | 시오 |\n| Dahee | 다희 | K-Santa | 산타 할아버지 | Siwon | 시원 |\n| Dahyeon | 다현 | Kangil | 강일 | Siwoo | 시우 |\n| Damian | 데이미언 | Kanno | 칸노 | Siyeon | 시연 |\n| Dan | 댄 | Katie | 케이티 | Skylar | 스카일러 |\n| Dana | 다나 | Kelly | 켈리 | Slushy | 슬러시 |\n| Dasom | 다솜 | Kelsey | 켈시 | Smoke | 스모크 |\n| David | 데이비드 | Kevin | 케빈 | Sohye | 소혜 |\n| Dean | 딘 | Keybo | 키보 | Sojin | 소진 |\n| Deokhwan | 덕환 | Kijang Kim | 김기장 | Soobin | 수빈 |\n| Dohan | 도한 | Killian the Vampire | 뱀파이어 킬리언 | Sookhee | 숙희 |\n| Dohee | 도희 | Kiseob | 기섭 | Sooni | 순이 |\n| Dohyun | 도현 | Klip Kim | 클립 킴 | Sophia | 소피아 |\n| Dollar Jr. | 달러 주니어 | Koombo | 쿰보 | Sora | 소라 |\n| Doug | 더그 | Kristen | 크리스틴 | Soye | 소예 |\n| Doughnut | 도넛 | Kukhee | 국희 | Soyi | 소이 |\n| DU5T | 더스트 | Kwonil | 권일 | Soyoung | 소영 |\n| Duckchun | 덕춘 | Kyumin | 규민 | Soyul | 소율 |\n| Duke | 듀크 | Kyungho | 경호 | Spice | 스파이스 |\n| Dukgu | 덕구 | Kyungsoo | 경수 | Sportscaster Kang | 강수정 캐스터 |\n| Duman | 두만 | Kyungsook | 경숙 | Sportscaster Tony | 이영광 캐스터 |\n| Dupil | 곽두필 | Lady Cho | 단희빈 | Starling | 스탈링 |\n| DVZY | 데이지 | Lala | 라라 | Stephanie | 스테파니 |\n| Dylan | 딜런 | Lamie | 라미 | Storyteller Jinsub | 송진섭 스토리텔러 |\n| E-seller Sherri | 쇼린이 | Landon | 랜던 | Sua | 수아 |\n| Echo | 에코 | Larry | 래리 | Suho | 수호 |\n| Edward | 에드워드 | Leo | 레오 | Suji | 수지 |\n| Elias | 엘리아스 | Liam | 리암 | Sujin | 수진 |\n| Elise | 엘리스 | Lindsay | 린지 | Sullock Hong | 홍설록 |\n| Ella | 엘라 | Liz | 리즈 | Sumin | 수민 |\n| Eman | 이만 | Lloyd | 로이드 | Sungbae | 성배 |\n| Emma | 엠마 | Logan | 로건 | Sunggyu | 성규 |\n| Eunbin | 은빈 | Lucille | 루실 | Sungho | 성호 |\n| Eunchae | 은채 | Luna | 루나 | Sunghoon | 성훈 |\n| Eunha | 은하 | Lydia | 리디아 | Sunghyun | 성현 |\n| Eunsol | 은솔 | Maddie | 매디 | Sungjun | 홈쇼핑 성준 |\n| Frankenstein | 프랑켄슈타인 | Maisie | 메이시 | Sungkwon | 성권 |\n| Furnando | 터르난도 | Margaret | 마가렛 | Sungtae | 성태 |\n| Gaeul | 가을 | Margot | 마고 | Sungwook | 성욱 |\n| Gahee | 가희 | Matthew | 매튜 | Sunyoung | 선영 |\n| George | 조지 | MBTI EF (F) | MBTI EF 여 | Suyoon | 수윤 |\n| GeumHee | 금희 | MBTI EF (M) | MBTI EF 남 | Sylvia | 실비아 |\n| Geunhyeok | 근혁 | MBTI ET (F) | MBTI ET 여 | Taebaek | 장태백 |\n| Geunseok | 근석 | MBTI ET (M) | MBTI ET 남 | Taeji | 태지 |\n| Geunwoo | 근우 | MBTI IF (F) | MBTI IF 여 | Taejoong | 태중 |\n| Geunyeong | 근영 | MBTI IF (M) | MBTI IF 남 | Taemin | 태민 |\n| Ggami | 까미 | MBTI IT (F) | MBTI IT 여 | Taesub | 태섭 |\n| Ggomi | 꼬미 | MBTI IT (M) | MBTI IT 남 | Taewoo | 태우 |\n| Glenda | 글렌다 | MC Kong | MC콩 | Teal | 틸 |\n| Goat Kim | 김고트 | MC TypeCast | MC 타캐 | Tessa | 테사 |\n| Gongchul | 공철 | Mia | 미아 | Tian | 티안 |\n| Gowoon | 고운 | Michael | 마이클 | Tim | 팀 |\n| Grace | 그레이스 | Mija | 오미자 | Tina | 티나 |\n| Graham | 그레이엄 | Mijin | 미진 | Toby | 토비 |\n| Gunseok | 건석 | Mikyung | 미경 | Tommy | 토미 |\n| Gunwoo | 건우 | Millie | 밀리 | Tyson | 타이슨 |\n| Guri | 구리 | Minchae | 민채 | UiChan | 의찬이 |\n| Gus | 거스 | Minji | 민지 | Uncle Hank | 행크 |\n| Ha Eun | 하은 | Minjoon | 민준 | Valerie | 밸러리 |\n| Haejun | 해준 | Minju | 민주 | Valkyrie | 발키리 |\n| Haerang | 해랑 | Minjung | 민정 | Vanessa | 바네사 |\n| Hailey | 헤일리 | Minsang | 민상 | Verna | 베르나 |\n| Hajun | 하준 | Minsu | 민수 | Victoria | 빅토리아 |\n| Hakchul | 학철 | Mio | 미오 | Viktor | 빅토르 |\n| Hamchu | 햄쮸 | Miran Choi | 최미란 | Vincent | 빈센트 |\n| Han Taesung Caster | 한태성 캐스터 | Mirine | 미리내 | Vivien | 비비안 |\n| Hana | 하나 | Miseon | 미선 | Wade | 웨이드 |\n| Hangyeol | 한결 | Miso | 미소 | Walter | 월터 |\n| Hanjun | 한준 | Mister Gop | 미스터 갑 | Wangkwon | 왕권 |\n| Hanna | 한나 | Monggun | 몽군 | Weather Reporter Sky | 하늘 캐스터 |\n| Hans | 한스 | Moonjung | 문정 | West | 웨스트 |\n| Hansol | 한솔 | Mooyeol | 무열 | Wonho | 원호 |\n| Hanyoung | 한영 | Morgan | 모건 | Wonkyung | 원경 |\n| Harin | 하린 | Moru | 머루 | Wonwoo | 원우 |\n| Harper | 하퍼 | Mrs. Claus | 미세스 클로스 | Wooju | 우주 |\n| Hayul | 하율 | Munseok | 문석 | Woosung | 우성 |\n| Heejoon | 희준 | Munsu | 문수 | Xavier | 자비에르 |\n| Helena | 헬레나 | Myeonghee | 명희 | Yeeun | 예은 |\n| Henry | 헨리 | Myungil | 명일 | Yejin | 예진 |\n| Homun Sim | 심호문 | Myungjoo | 명주 | Yejoon | 예준 |\n| Hoon | 훈 | Naeun | 나은 | YeLin | 예린 |\n| Hosik | 호식 | Najin | 나진 | Yena | 예나 |\n| Hosun | 호선 | Namjoon | 남준 | Yeonah | 연아 |\n| Hoyoung | 호영 | Nana | 나나 | Yeonggeol | 영걸 |\n| Hugh | 휴 | Nari | 개나리 | Yeonhwa | 연화 |\n| Hwimin | 휘민 | Nathan | 네이선 | Yeonja | 연자 |\n| Hyejung | 혜정 | Neel | 닐 | Yeonsuh | 연서 |\n| Hyelee | 혜리 | Neoguard | 네오가드 | Yeonwoo | 연우 |\n| Hyemin | 혜민 | News anchor Jinsub | 송진섭 앵커 | Yeseul | 예슬 |\n| Hyena | 혜나 | Newscaster John | 존 앵커 | Yongsik | 용식이 |\n| Hyeongjin | 형진 | Nia | 니아 | Yoonseo | 윤서 |\n| Hyera | 혜라 | Noa | 노아 | Yoonsung | 윤성 |\n| Hyesu | 홈쇼핑 혜수 | Noel | 노엘 | Younggil | 영길 |\n| Hyewon | 혜원 | Noeul | 노을 | Younghee | 영희 |\n| Hyun | 현 | Norah | 노라 | Younghwan | 영환 |\n| Hyunji | 현지 | Nova | 노바 | Youngji | 영지 |\n| Hyunjin | 현진 | Old radio | 레디오 | Youngkyu | 영규 |\n| Hyunju | 현주 | Oliver | 올리버 | Youngmok | 영목 |\n| Hyunkyung | 현경 | Olivia | 올리비아 | Yubin | 유빈 |\n| Hyunmin | 현민 | Oscar | 오스카 | Yujin | 유진 |\n| Hyunseung | 현승 | Owen | 오언 | Yumi | 유미 |\n| HyunWoo | 현우 | P-0150N | 포이즌 | Yumin | 유민 |\n| Ian | 이안 | Pacang | 파캉 | Yunbin | 윤빈 |\n| Icarus | 이카루스 | Paige | 페이지 | Yunjeong | 윤정 |\n| Igyeom | 이겸 | Pangpang | 팡팡 | Yura | 유라 |\n| Ijun | 이준 | Patricia | 패트리샤 | Yuri | 유리 |\n| Ilho | 일호 | Patrick | 패트릭 | Yuseong | 유성 |\n| Ina | 이나 | Peter | 피터 | Zoey | 조이 |\n| Inhwa | 인화 | Philip | 필립 | | |\n\n
\n" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "사람이 읽을 수 있는 보이스 이름" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입 — `original` 은 타입캐스트가 기본 제공하는 스톡 보이스, `custom` 은 사용자가 업로드/클론한 보이스입니다." } }, "description": "모델별로 그룹화된 감정과 향상된 메타데이터가 있는 V2 보이스 응답 모델" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대입니다." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별(`male` 또는 `female`)입니다." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "지원하는 TTS 모델과 감정 목록입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유 보이스 식별자입니다. 기본 보이스는 `tc_`, 커스텀 보이스는 `uc_`로 시작합니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "추천 사용 사례 목록입니다." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "`{\"eng\": \"Daejin\", \"kor\": \"대진\"}` 같은 다국어 보이스 이름입니다." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입(`original` 또는 `custom`)입니다." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "제공되는 경우 사용할 수 있는 미리듣기 오디오 URL입니다." } }, "description": "다국어 이름과 미리듣기 URL을 포함한 보이스 정보입니다." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "API 플랜.\n\n사용 가능한 값:\n- **free**: 제한된 크레딧의 무료 등급\n- **lite**: 중간 수준 크레딧의 라이트 플랜\n- **plus**: 높은 크레딧의 플러스 플랜\n- **custom**: 커스텀 엔터프라이즈 플랜" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "음성 합성에 사용할 TTS 모델 버전. 다양한 모델은 다양한 기능과 품질 수준을 제공합니다.\n\n사용 가능한 모델:\n- **ssfm-v30**: 향상된 플로우와 추가 감정 프리셋이 있는 최신 모델(권장)\n- **ssfm-v21**: 검증된 신뢰성과 일관된 품질을 갖춘 안정적인 모델\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS 모델 버전(예: ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "이 모델에서 지원되는 감정 목록" } }, "description": "버전 및 지원되는 감정을 포함한 모델 정보" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "영어 보이스 이름입니다." }, "kor": { "type": "string", "title": "Kor", "description": "한국어 보이스 이름입니다." } }, "description": "ISO 639-3 언어 코드를 키로 사용하는 다국어 보이스 이름입니다." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "보이스 타입 분류.\n\n- `original` — 타입캐스트가 기본 제공하는 스톡 보이스로, 모든 계정에서 사용 가능합니다.\n- `custom` — 사용자가 본인의 샘플을 업로드하거나 클로닝해서 만든 보이스입니다." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "성별 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **male**: 남성 보이스\n- **female**: 여성 보이스\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "음성 합성에 사용 가능한 감정 프리셋. 각 감정은 생성된 음성의 톤, 속도, 표현력에 영향을 줍니다.\n\n**ssfm-v21 지원 감정 (4종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n\n**ssfm-v30 지원 감정 (7종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n- whisper: 부드럽고 조용한 말\n- toneup: 더 높은 톤 강조\n- tonedown: 더 낮은 톤 강조\n\n/v2/voices API 응답을 통해 각 음성에 사용 가능한 감정을 확인하세요.\n" }, "SmartPrompt": { "type": "object", "title": "스마트 프롬프트 (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "TTSRequest의 `text` 필드 이후에 오는 텍스트. 감정 추론을 위한 전방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 전환을 예측하는 데 도움\r\n- 다음 컨텍스트가 없으면 비워 둡니다\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "프롬프트 유형을 식별하는 판별자 필드. 컨텍스트 인식 감정 추론을 위해 \"smart\"로 설정해야 합니다.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "TTSRequest의 `text` 필드 이전에 오는 텍스트. 감정 추론을 위한 후방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 빌드업과 컨텍스트를 이해하는 데 도움\r\n- 이전 컨텍스트가 없으면 비워 둡니다\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "프리셋 프롬프트 (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "프롬프트 유형을 식별하는 판별자 필드. 프리셋 기반 감정 제어를 위해 \"preset\"으로 설정해야 합니다.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "생성된 음성에 적용할 감정 프리셋.\r\n\r\n지원되는 감정: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "생성된 음성의 감정 표현 강도를 제어합니다.\r\n\r\n- 0.0: 완전히 중립적, 감정 색채 없음\r\n- 0.5: 미묘한 감정 힌트\r\n- 1.0: 표준 감정 표현(기본값)\r\n- 1.5: 강한 감정 강조\r\n- 2.0: 최대 강도, 매우 표현력 있음\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "콘텐츠 유형 필터링을 위한 보이스 사용 사례 카테고리. 각 보이스는 특정 콘텐츠 유형에 대한 적합성을 나타내는 하나 이상의 사용 사례로 태그됩니다.\n\n**사용 가능한 사용 사례:**\n- **Announcer**: 공공 발표 및 프레젠테이션\n- **Anime**: 애니메이션 보이스\n- **Audiobook**: 장문 내레이션 및 스토리텔링\n- **Conversational**: 챗봇 및 대화형 AI\n- **Documentary**: 다큐멘터리 내레이션 및 해설\n- **E-learning**: 교육 콘텐츠 및 튜토리얼\n- **Rapper**: 랩 및 음악 퍼포먼스\n- **Game**: 비디오 게임 보이스 및 내레이션\n- **Tiktok/Reels**: SNS 숏츠\n- **News**: 뉴스 방송\n- **Podcast**: 방송 및 팟캐스트 제작\n- **Voicemail**: IVR 시스템 및 음성 비서\n- **Ads**: 광고 및 홍보 콘텐츠\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "문제를 설명하는 오류 메시지" }, "message": { "type": "string", "description": "구조화된 오류에 대한 설명" }, "error_code": { "type": "string", "description": "구조화된 오류를 식별하는 코드" } }, "description": "기존 오류와 유효성 검사 오류는 `detail`을 사용하며, 구조화된 오류는 `error_code`와 `message`를 사용합니다." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "`status`가 `failed`일 때의 실패 사유입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "source": { "type": "string", "title": "Source", "description": "생성 방식(`instant` 또는 `professional`)입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "UTC 기준 생성 시각입니다." } }, "description": "인증된 계정이 소유한 커스텀 보이스입니다." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "추천 관련도 점수. 값이 높을수록 검색어와 더 잘 맞는 후보입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "`tc_` prefix 가 붙은 타입캐스트 보이스 식별자. 텍스트 음성 변환 요청의 `voice_id` 로 사용할 수 있습니다." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "사람이 읽을 수 있는 보이스 이름." } }, "description": "`GET /v1/voices/recommendations` 응답의 보이스 추천 후보. 관련도 순서로 정렬됩니다." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "커스텀 보이스의 현재 생성 또는 학습 상태입니다." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름 (1~30자)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "보이스가 클로닝된 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "`uc_` prefix 가 붙은 커스텀 보이스 식별자. `POST /v1/text-to-speech` 등 `voice_id` 를 받는 엔드포인트에 그대로 사용할 수 있습니다." } }, "description": "`POST /v1/voices/clone` 응답 — 퀵 클로닝으로 생성된 커스텀 보이스 메타데이터." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "세그먼트 구분값입니다. 항상 `pause`입니다." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "초 단위 쉼 길이입니다. 개별 쉼은 최대 10초, 모든 쉼의 합은 최대 60초입니다.", "exclusiveMinimum": 0 } }, "description": "크레딧 차감 없이 삽입되는 무음 구간입니다.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 단어 단위 정렬 구간." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "현재 구독 플랜명" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "사용 제한 정보" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "크레딧 사용 정보" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "구독 정보 응답 모델" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호/공백 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 문자 단위 정렬 구간." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." } }, "description": "퀵 클로닝 또는 프리미엄 클로닝 생성 요청 결과입니다." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "base64 로 인코딩된 오디오 바이트. `audio_format` 확장자로 디코딩해 파일로 저장할 수 있습니다." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "단어 단위 타임스탬프(문장부호 포함). 요청이 `granularity=char` 일 때는 `null`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "문자 단위 타임스탬프(문장부호와 공백 포함). 요청이 `granularity=word` 일 때는 `null`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "`audio` 필드의 오디오 인코딩 포맷 — `wav` 또는 `mp3` (요청의 `output.audio_format` 에 따라 결정)." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "생성된 오디오의 길이(초)." } }, "description": "TTS 생성 + 타임스탬프 정렬 통합 응답." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "오디오 샘플. WAV 또는 MP3, 최대 25MB, 5초 이상 150초 이하.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "보이스 이름 (1~30자)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "클로닝할 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." } }, "description": "퀵 클로닝 요청을 위한 multipart 본문." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "파일", "format": "binary", "description": "25 MiB 이하이면서 길이가 5초 이상 150초 이하인 WAV 또는 MP3 음성 파일 1개입니다.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "상품 내레이터", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." } }, "description": "퀵 클로닝용 multipart 요청 본문입니다." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "커스텀 보이스 이름", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "파일", "description": "학습에 사용할 WAV 또는 MP3 음성 파일입니다. 하나의 파일을 업로드할 수 있습니다.\r\n\r\n**업로드 제한**\r\n\r\n* WAV 또는 MP3 파일 1개\r\n* 파일 크기: 1 GiB 이하\r\n* 길이: 5분 이상 3시간 이하\r\n* 샘플레이트: 16 kHz 이상\r\n\r\n최상의 결과를 위해 다음 조건을 충족하는 오디오를 권장합니다.\r\n\r\n* 생성된 보이스가 재현하기를 원하는 말투와 최대한 가깝게 녹음해 주세요.\r\n* 배경 소음이 없는 조용한 환경에서 녹음해 주세요.\r\n* 한 명의 화자 목소리만 포함해 주세요.\r\n* `language` 필드에 지정한 언어로 녹음해 주세요.\r\n* 오디오 길이가 길수록 생성되는 보이스의 품질이 향상됩니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." }, "language": { "type": "string", "title": "Language", "example": "kor", "description": "`kor`, `eng` 같은 ISO 639-3 언어 코드입니다." } }, "description": "프리미엄 클로닝용 multipart 요청 본문입니다." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "인증을 위한 API 키. 타입캐스트 API 콘솔에서 API 키를 생성할 수 있습니다." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 커스텀 보이스 상세 정보 조회 > 인증된 계정이 소유한 삭제되지 않은 커스텀 보이스 한 개를 조회합니다. 프리미엄 클로닝을 시작한 뒤 `status`가 `completed` 또는 `failed`가 될 때까지 이 API를 호출하세요. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "프로덕션 서버" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/custom-voices/{voice_id}": { "get": { "tags": [ "Custom Voices" ], "x-mint": { "href": "/ko/api-reference/custom-voices/get-custom-voice" }, "summary": "커스텀 보이스 상세 정보 조회", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomVoiceItem" }, "example": { "name": "브랜드 보이스", "error": null, "model": "ssfm-v30", "source": "professional", "status": "training", "voice_id": "uc_6700000000000000000000bb", "created_at": "2026-08-26T04:20:00Z" } } }, "description": "커스텀 보이스 상세 정보를 조회했습니다." }, "400": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Invalid voice ID", "error_code": "INVALID_VOICE_ID" } } }, "description": "커스텀 보이스 ID 형식이 올바르지 않습니다." }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "인증에 실패했습니다." }, "404": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Custom voice not found", "error_code": "CUSTOM_VOICE_NOT_FOUND" } } }, "description": "커스텀 보이스가 없거나 삭제되었거나 다른 계정이 소유하고 있습니다." }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid request format" } } }, "description": "유효하지 않은 보이스 ID입니다." } }, "parameters": [ { "in": "path", "name": "voice_id", "schema": { "type": "string", "title": "Voice Id" }, "required": true, "description": "조회할 커스텀 보이스의 고유 식별자입니다." } ], "description": "인증된 계정이 소유한 삭제되지 않은 커스텀 보이스 한 개를 조회합니다. 프리미엄 클로닝을 시작한 뒤 `status`가 `completed` 또는 `failed`가 될 때까지 이 API를 호출하세요.", "operationId": "get_custom_voice_v1_custom_voices__voice_id__get", "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request GET \\\n --url https://api.typecast.ai/v1/custom-voices/uc_6700000000000000000000bb \\\n --header 'X-API-KEY: '\n" }, { "lang": "Python", "label": "Python (requests)", "source": "import requests\n\nresponse = requests.get(\n \"https://api.typecast.ai/v1/custom-voices/uc_6700000000000000000000bb\",\n headers={\"X-API-KEY\": \"\"},\n timeout=30,\n)\nresponse.raise_for_status()\nprint(response.json())\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "허용되는 최대 동시 요청 수" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "전체 커스텀 보이스 슬롯 한도 (professional 포함)" }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "사용 제한 정보" }, "Prompt": { "title": "프롬프트 (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "적용할 감정 프리셋.\r\n\r\nssfm-v21 지원 감정: normal, happy, sad, angry\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "example": 1, "description": "감정 표현 강도 제어(0.0~2.0).\r\n\r\n- 0.0: 완전히 중립적\r\n- 1.0: 표준 표현(기본값)\r\n- 2.0: 최대 강도\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "연령대 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **child**: 어린이 보이스(12세 미만)\n- **teenager**: 청소년 보이스(13-19세)\n- **young_adult**: 청년 보이스(20-35세)\n- **middle_age**: 중년 보이스(36-60세)\n- **elder**: 노년 보이스(60세 이상)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "플랜에서 기본으로 제공하는 총 크레딧" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "사용된 크레딧 수" } }, "description": "크레딧 사용 정보" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대 분류(어린이/청소년/청년/중년/노년)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별 분류(남성/여성)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "사용 가능한 감정이 있는 지원되는 TTS 모델 목록(예: [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유한 보이스 식별자. 기본 제공 보이스는 `tc_` prefix (예: `tc_60e5426de8b95f1d3000d7b5`), `POST /v1/voices/clone` 으로 만든 커스텀 보이스는 `uc_` prefix 를 사용합니다. 본인이 소유한 `uc_` 보이스도 `/v2/voices` 응답에 포함됩니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "이 보이스가 적합한 사용 사례 카테고리 목록\n\n## 보이스 이름 참고\n\n`voice_name` 필드는 영문으로만 반환됩니다. 아래 목록에서 한국어 표기를 확인하실 수 있습니다.\n\n
\n보이스 이름 목록 펼치기 (542개)\n\n| 영어 | 한국어 | 영어 | 한국어 | 영어 | 한국어 |\n| --- | --- | --- | --- | --- | --- |\n| Aaron | 에런 | Inhye | 인혜 | Piljae | 필재 |\n| Abigail | 아비게일 | Inseong | 인성 | Pyeonghwa | 평화 |\n| Ae-ran | 애란 | Instructor Han | 한유격 교관 | Rachel | 레이첼 |\n| Ael | 아엘 | Insun | 인선 | Ravi | 라비 |\n| Agatha | 아가사 | Jabbaba | 자바바 | Rayeon | 라연 |\n| Aiden | 에이든 | Jack | 잭 | Rebecca | 레베카 |\n| Alena | 알레나 | Jack-o’-Lantern | 잭오랜턴 | Reporter Bona | 다보나 기자 |\n| Alex | 알렉스 | Jackson | 잭슨 | Reporter Catalina | 카탈리나 기자 |\n| Amber | 엠버 | Jaeho | 재호 | Reporter Jinsub | 송진섭 기자 |\n| Anchor Hwa | 김경화 앵커 | Jaehun | 재훈 | Reporter Kang | 강수정 기자 |\n| Angela | 안젤라 | Jaejun | 재준 | Reporter Lee | 이승주 기자 |\n| Anja | 안냐 | Jaekyung | 재경 | Reporter Parkchan | 박찬 기자 |\n| Annabelle the Ghost | 귀신 애나벨 | Jaesun | 재선 | Rex | 렉스 |\n| Annie | 애니 | JaeYi | 재이 | Riley | 라일리 |\n| April | 에이프릴 | Jaeyoung | 재영 | Risan Ji | 지리산 |\n| Arang | 아랑 | Jain | 자인 | Rita | 리타 |\n| Ari | 아리 | Jake | 제이크 | River | 리버 |\n| Arin | 아린 | Janet | 재닛 | Robert | 로버트 |\n| Athena | 아테나 | Jangho | 장호 | Robo | 로보 |\n| Audrey | 오드리 | Jangwoon | 장운 | Romi | 로미 |\n| Avery | 에이버리 | Jechan | 제찬 | Ron | 론 |\n| Avong | 아봉 | Jeff | 제프 | Roro | 로로 |\n| Azzi | 아찌 | Jenna | 제나 | Rowoon | 로운 |\n| Babilon | 바빌론 | Jennifer | 제니퍼 | Royce | 로이스 |\n| Bell | 벨 | Jeong Choi | 최정 | Rudolph | 루돌프 |\n| Ben | 벤 | Jeongah | 정아 | Ruri | 루리 |\n| Benny | 베니 | JeongJin | 정진 | Rusty | 러스티 |\n| Beri | 베리 | Jeongseob | 정섭 | Ryan | 라이언 |\n| Billie | 빌리 | Jerome | 제롬 | Ryueun | 류은 |\n| Bomi | 이보미 | Jewby | 쥬비 | Sabrina the Witch | 마녀 사브리나 |\n| Bonggyu | 봉규 | Jian | 지안 | Saeron | 새론 |\n| Bongman Kim | 김봉만 | Jicheol | 지철 | Salty Chan-gu | 삐뚤어진 찬구 |\n| Bono | 보노 | Jihee | 지희 | Samantha | 사만다 |\n| Bora | 보라 | Jihoon | 지훈 | Sammy | 새미 |\n| Brad | 브래드 | Jihyun | 지현 | Sangdo | 상도 |\n| Bruce | 브루스 | Jimmy | 지미 | Sanghoon | 상훈 |\n| Buddy | 버디 | Jin | 진 | Sangwoo | 상우 |\n| Bumsoo | 범수 | Jinhan | 진한 | Santa | 산타 |\n| Buttaguy | 빠다가이 | Jinhang Kim | 김진행 | Santa Claus | 산타클로스 |\n| Caitlyn | 케이틀린 | Jinhee | 진희 | Santa Reporter | VJ 싼타 |\n| Callan | 칼란 | Jinhyuk | 진혁 | Sara | 세라 |\n| Camila | 카밀라 | Jinseo | 진서 | Sean | 션 |\n| Captain Bill | 캡틴 빌 | Jinung | 진웅 | SeHee | 세희 |\n| Carl | 칼 | Jinwoo | 진우 | Seheon | 세헌 |\n| Carlos | 카를로스 | Jiwoo | 지우 | Sejin | 세진 |\n| Carol | 캐롤 | Jiyoon | 지윤 | Sena | 세나 |\n| Catherine | 캐서린 | Jiyoung | 지영 | Seohee | 서희 |\n| Chad | 채드 | Jodie | 조디 | Seojin | 서진 |\n| Chaeah | 채아 | Jolly | 졸리 | Seojoon | 서준 |\n| Chan-gu | 찬구 | Jongdae | 종대 | Seok Choi | 최석 |\n| Changbae | 창배 | Jooeun | 주은 | SeokPil | 한석필 |\n| Changhee | 창희 | Joonghyun | 중현 | Seokpyo | 석표 |\n| Changmin | 창민 | Joongsik | 중식 | Seolhwa | 설화 |\n| Changsu | 박창수 | Joonkyu | 준규 | Seonha | 선하 |\n| Chanhyuk | 찬혁 | Joshua | 조슈아 | Seoyeon | 서연 |\n| Charlotte | 샬롯 | Juha | 주하 | Seoyoon | 서윤 |\n| Chase | 체이스 | Juho | 주호 | Seungah | 승아 |\n| Cheolhoon | 철훈 | Jungbong | 정봉 | Seungheon | 승헌 |\n| Cheolyong | 리철용 | Junghee | 정희 | Seungho | 승호 |\n| Cherry | 체리 | Jungjae | 정재 | Seunghwa | 승화 |\n| Chester | 체스터 | Jungmin | 정민 | Seungjae | 승재 |\n| Chiho | 치호 | Jungseok | 정석 | Seungmoon | 승문 |\n| Chloe | 클로이 | Jungsoon | 정순 | Seungwon | 승원 |\n| Choyeon | 강초연 교관 | Jungwon | 정원 | Seungyeon | 승연 |\n| Chungah | 청아 | Junhee | 준희 | Sewoo | 세우 |\n| Chunsik Kang | 강춘식 기자 | Junho | 준호 | Shana | 샤나 |\n| Claire | 클레어 | JunKi | 준기 | Sheriff Kim | 김반장 |\n| Classic Narrator | 미스타 변사 | Junsang | 준상 | Shinhe | 신혜 |\n| Cole | 콜 | Junseong | 준성 | Shinwook | 신욱 |\n| Cox | 콕스 | Justice Roh | 정의로 | Shotgun | 샷건 |\n| Cyrus | 사이러스 | Justin | 저스틴 | Simon | 사이먼 |\n| Dabin | 다빈 | Juwon | 주원 | Sindarin | 신다린 |\n| Daegil | 대길 | Juyoung | 주영 | Sio | 시오 |\n| Dahee | 다희 | K-Santa | 산타 할아버지 | Siwon | 시원 |\n| Dahyeon | 다현 | Kangil | 강일 | Siwoo | 시우 |\n| Damian | 데이미언 | Kanno | 칸노 | Siyeon | 시연 |\n| Dan | 댄 | Katie | 케이티 | Skylar | 스카일러 |\n| Dana | 다나 | Kelly | 켈리 | Slushy | 슬러시 |\n| Dasom | 다솜 | Kelsey | 켈시 | Smoke | 스모크 |\n| David | 데이비드 | Kevin | 케빈 | Sohye | 소혜 |\n| Dean | 딘 | Keybo | 키보 | Sojin | 소진 |\n| Deokhwan | 덕환 | Kijang Kim | 김기장 | Soobin | 수빈 |\n| Dohan | 도한 | Killian the Vampire | 뱀파이어 킬리언 | Sookhee | 숙희 |\n| Dohee | 도희 | Kiseob | 기섭 | Sooni | 순이 |\n| Dohyun | 도현 | Klip Kim | 클립 킴 | Sophia | 소피아 |\n| Dollar Jr. | 달러 주니어 | Koombo | 쿰보 | Sora | 소라 |\n| Doug | 더그 | Kristen | 크리스틴 | Soye | 소예 |\n| Doughnut | 도넛 | Kukhee | 국희 | Soyi | 소이 |\n| DU5T | 더스트 | Kwonil | 권일 | Soyoung | 소영 |\n| Duckchun | 덕춘 | Kyumin | 규민 | Soyul | 소율 |\n| Duke | 듀크 | Kyungho | 경호 | Spice | 스파이스 |\n| Dukgu | 덕구 | Kyungsoo | 경수 | Sportscaster Kang | 강수정 캐스터 |\n| Duman | 두만 | Kyungsook | 경숙 | Sportscaster Tony | 이영광 캐스터 |\n| Dupil | 곽두필 | Lady Cho | 단희빈 | Starling | 스탈링 |\n| DVZY | 데이지 | Lala | 라라 | Stephanie | 스테파니 |\n| Dylan | 딜런 | Lamie | 라미 | Storyteller Jinsub | 송진섭 스토리텔러 |\n| E-seller Sherri | 쇼린이 | Landon | 랜던 | Sua | 수아 |\n| Echo | 에코 | Larry | 래리 | Suho | 수호 |\n| Edward | 에드워드 | Leo | 레오 | Suji | 수지 |\n| Elias | 엘리아스 | Liam | 리암 | Sujin | 수진 |\n| Elise | 엘리스 | Lindsay | 린지 | Sullock Hong | 홍설록 |\n| Ella | 엘라 | Liz | 리즈 | Sumin | 수민 |\n| Eman | 이만 | Lloyd | 로이드 | Sungbae | 성배 |\n| Emma | 엠마 | Logan | 로건 | Sunggyu | 성규 |\n| Eunbin | 은빈 | Lucille | 루실 | Sungho | 성호 |\n| Eunchae | 은채 | Luna | 루나 | Sunghoon | 성훈 |\n| Eunha | 은하 | Lydia | 리디아 | Sunghyun | 성현 |\n| Eunsol | 은솔 | Maddie | 매디 | Sungjun | 홈쇼핑 성준 |\n| Frankenstein | 프랑켄슈타인 | Maisie | 메이시 | Sungkwon | 성권 |\n| Furnando | 터르난도 | Margaret | 마가렛 | Sungtae | 성태 |\n| Gaeul | 가을 | Margot | 마고 | Sungwook | 성욱 |\n| Gahee | 가희 | Matthew | 매튜 | Sunyoung | 선영 |\n| George | 조지 | MBTI EF (F) | MBTI EF 여 | Suyoon | 수윤 |\n| GeumHee | 금희 | MBTI EF (M) | MBTI EF 남 | Sylvia | 실비아 |\n| Geunhyeok | 근혁 | MBTI ET (F) | MBTI ET 여 | Taebaek | 장태백 |\n| Geunseok | 근석 | MBTI ET (M) | MBTI ET 남 | Taeji | 태지 |\n| Geunwoo | 근우 | MBTI IF (F) | MBTI IF 여 | Taejoong | 태중 |\n| Geunyeong | 근영 | MBTI IF (M) | MBTI IF 남 | Taemin | 태민 |\n| Ggami | 까미 | MBTI IT (F) | MBTI IT 여 | Taesub | 태섭 |\n| Ggomi | 꼬미 | MBTI IT (M) | MBTI IT 남 | Taewoo | 태우 |\n| Glenda | 글렌다 | MC Kong | MC콩 | Teal | 틸 |\n| Goat Kim | 김고트 | MC TypeCast | MC 타캐 | Tessa | 테사 |\n| Gongchul | 공철 | Mia | 미아 | Tian | 티안 |\n| Gowoon | 고운 | Michael | 마이클 | Tim | 팀 |\n| Grace | 그레이스 | Mija | 오미자 | Tina | 티나 |\n| Graham | 그레이엄 | Mijin | 미진 | Toby | 토비 |\n| Gunseok | 건석 | Mikyung | 미경 | Tommy | 토미 |\n| Gunwoo | 건우 | Millie | 밀리 | Tyson | 타이슨 |\n| Guri | 구리 | Minchae | 민채 | UiChan | 의찬이 |\n| Gus | 거스 | Minji | 민지 | Uncle Hank | 행크 |\n| Ha Eun | 하은 | Minjoon | 민준 | Valerie | 밸러리 |\n| Haejun | 해준 | Minju | 민주 | Valkyrie | 발키리 |\n| Haerang | 해랑 | Minjung | 민정 | Vanessa | 바네사 |\n| Hailey | 헤일리 | Minsang | 민상 | Verna | 베르나 |\n| Hajun | 하준 | Minsu | 민수 | Victoria | 빅토리아 |\n| Hakchul | 학철 | Mio | 미오 | Viktor | 빅토르 |\n| Hamchu | 햄쮸 | Miran Choi | 최미란 | Vincent | 빈센트 |\n| Han Taesung Caster | 한태성 캐스터 | Mirine | 미리내 | Vivien | 비비안 |\n| Hana | 하나 | Miseon | 미선 | Wade | 웨이드 |\n| Hangyeol | 한결 | Miso | 미소 | Walter | 월터 |\n| Hanjun | 한준 | Mister Gop | 미스터 갑 | Wangkwon | 왕권 |\n| Hanna | 한나 | Monggun | 몽군 | Weather Reporter Sky | 하늘 캐스터 |\n| Hans | 한스 | Moonjung | 문정 | West | 웨스트 |\n| Hansol | 한솔 | Mooyeol | 무열 | Wonho | 원호 |\n| Hanyoung | 한영 | Morgan | 모건 | Wonkyung | 원경 |\n| Harin | 하린 | Moru | 머루 | Wonwoo | 원우 |\n| Harper | 하퍼 | Mrs. Claus | 미세스 클로스 | Wooju | 우주 |\n| Hayul | 하율 | Munseok | 문석 | Woosung | 우성 |\n| Heejoon | 희준 | Munsu | 문수 | Xavier | 자비에르 |\n| Helena | 헬레나 | Myeonghee | 명희 | Yeeun | 예은 |\n| Henry | 헨리 | Myungil | 명일 | Yejin | 예진 |\n| Homun Sim | 심호문 | Myungjoo | 명주 | Yejoon | 예준 |\n| Hoon | 훈 | Naeun | 나은 | YeLin | 예린 |\n| Hosik | 호식 | Najin | 나진 | Yena | 예나 |\n| Hosun | 호선 | Namjoon | 남준 | Yeonah | 연아 |\n| Hoyoung | 호영 | Nana | 나나 | Yeonggeol | 영걸 |\n| Hugh | 휴 | Nari | 개나리 | Yeonhwa | 연화 |\n| Hwimin | 휘민 | Nathan | 네이선 | Yeonja | 연자 |\n| Hyejung | 혜정 | Neel | 닐 | Yeonsuh | 연서 |\n| Hyelee | 혜리 | Neoguard | 네오가드 | Yeonwoo | 연우 |\n| Hyemin | 혜민 | News anchor Jinsub | 송진섭 앵커 | Yeseul | 예슬 |\n| Hyena | 혜나 | Newscaster John | 존 앵커 | Yongsik | 용식이 |\n| Hyeongjin | 형진 | Nia | 니아 | Yoonseo | 윤서 |\n| Hyera | 혜라 | Noa | 노아 | Yoonsung | 윤성 |\n| Hyesu | 홈쇼핑 혜수 | Noel | 노엘 | Younggil | 영길 |\n| Hyewon | 혜원 | Noeul | 노을 | Younghee | 영희 |\n| Hyun | 현 | Norah | 노라 | Younghwan | 영환 |\n| Hyunji | 현지 | Nova | 노바 | Youngji | 영지 |\n| Hyunjin | 현진 | Old radio | 레디오 | Youngkyu | 영규 |\n| Hyunju | 현주 | Oliver | 올리버 | Youngmok | 영목 |\n| Hyunkyung | 현경 | Olivia | 올리비아 | Yubin | 유빈 |\n| Hyunmin | 현민 | Oscar | 오스카 | Yujin | 유진 |\n| Hyunseung | 현승 | Owen | 오언 | Yumi | 유미 |\n| HyunWoo | 현우 | P-0150N | 포이즌 | Yumin | 유민 |\n| Ian | 이안 | Pacang | 파캉 | Yunbin | 윤빈 |\n| Icarus | 이카루스 | Paige | 페이지 | Yunjeong | 윤정 |\n| Igyeom | 이겸 | Pangpang | 팡팡 | Yura | 유라 |\n| Ijun | 이준 | Patricia | 패트리샤 | Yuri | 유리 |\n| Ilho | 일호 | Patrick | 패트릭 | Yuseong | 유성 |\n| Ina | 이나 | Peter | 피터 | Zoey | 조이 |\n| Inhwa | 인화 | Philip | 필립 | | |\n\n
\n" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "사람이 읽을 수 있는 보이스 이름" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입 — `original` 은 타입캐스트가 기본 제공하는 스톡 보이스, `custom` 은 사용자가 업로드/클론한 보이스입니다." } }, "description": "모델별로 그룹화된 감정과 향상된 메타데이터가 있는 V2 보이스 응답 모델" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대입니다." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별(`male` 또는 `female`)입니다." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "지원하는 TTS 모델과 감정 목록입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유 보이스 식별자입니다. 기본 보이스는 `tc_`, 커스텀 보이스는 `uc_`로 시작합니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "추천 사용 사례 목록입니다." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "`{\"eng\": \"Daejin\", \"kor\": \"대진\"}` 같은 다국어 보이스 이름입니다." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입(`original` 또는 `custom`)입니다." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "제공되는 경우 사용할 수 있는 미리듣기 오디오 URL입니다." } }, "description": "다국어 이름과 미리듣기 URL을 포함한 보이스 정보입니다." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "API 플랜.\n\n사용 가능한 값:\n- **free**: 제한된 크레딧의 무료 등급\n- **lite**: 중간 수준 크레딧의 라이트 플랜\n- **plus**: 높은 크레딧의 플러스 플랜\n- **custom**: 커스텀 엔터프라이즈 플랜" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "음성 합성에 사용할 TTS 모델 버전. 다양한 모델은 다양한 기능과 품질 수준을 제공합니다.\n\n사용 가능한 모델:\n- **ssfm-v30**: 향상된 플로우와 추가 감정 프리셋이 있는 최신 모델(권장)\n- **ssfm-v21**: 검증된 신뢰성과 일관된 품질을 갖춘 안정적인 모델\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS 모델 버전(예: ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "이 모델에서 지원되는 감정 목록" } }, "description": "버전 및 지원되는 감정을 포함한 모델 정보" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "영어 보이스 이름입니다." }, "kor": { "type": "string", "title": "Kor", "description": "한국어 보이스 이름입니다." } }, "description": "ISO 639-3 언어 코드를 키로 사용하는 다국어 보이스 이름입니다." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "보이스 타입 분류.\n\n- `original` — 타입캐스트가 기본 제공하는 스톡 보이스로, 모든 계정에서 사용 가능합니다.\n- `custom` — 사용자가 본인의 샘플을 업로드하거나 클로닝해서 만든 보이스입니다." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "성별 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **male**: 남성 보이스\n- **female**: 여성 보이스\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "음성 합성에 사용 가능한 감정 프리셋. 각 감정은 생성된 음성의 톤, 속도, 표현력에 영향을 줍니다.\n\n**ssfm-v21 지원 감정 (4종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n\n**ssfm-v30 지원 감정 (7종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n- whisper: 부드럽고 조용한 말\n- toneup: 더 높은 톤 강조\n- tonedown: 더 낮은 톤 강조\n\n/v2/voices API 응답을 통해 각 음성에 사용 가능한 감정을 확인하세요.\n" }, "SmartPrompt": { "type": "object", "title": "스마트 프롬프트 (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "TTSRequest의 `text` 필드 이후에 오는 텍스트. 감정 추론을 위한 전방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 전환을 예측하는 데 도움\r\n- 다음 컨텍스트가 없으면 비워 둡니다\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "프롬프트 유형을 식별하는 판별자 필드. 컨텍스트 인식 감정 추론을 위해 \"smart\"로 설정해야 합니다.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "TTSRequest의 `text` 필드 이전에 오는 텍스트. 감정 추론을 위한 후방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 빌드업과 컨텍스트를 이해하는 데 도움\r\n- 이전 컨텍스트가 없으면 비워 둡니다\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "프리셋 프롬프트 (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "프롬프트 유형을 식별하는 판별자 필드. 프리셋 기반 감정 제어를 위해 \"preset\"으로 설정해야 합니다.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "생성된 음성에 적용할 감정 프리셋.\r\n\r\n지원되는 감정: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "생성된 음성의 감정 표현 강도를 제어합니다.\r\n\r\n- 0.0: 완전히 중립적, 감정 색채 없음\r\n- 0.5: 미묘한 감정 힌트\r\n- 1.0: 표준 감정 표현(기본값)\r\n- 1.5: 강한 감정 강조\r\n- 2.0: 최대 강도, 매우 표현력 있음\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "콘텐츠 유형 필터링을 위한 보이스 사용 사례 카테고리. 각 보이스는 특정 콘텐츠 유형에 대한 적합성을 나타내는 하나 이상의 사용 사례로 태그됩니다.\n\n**사용 가능한 사용 사례:**\n- **Announcer**: 공공 발표 및 프레젠테이션\n- **Anime**: 애니메이션 보이스\n- **Audiobook**: 장문 내레이션 및 스토리텔링\n- **Conversational**: 챗봇 및 대화형 AI\n- **Documentary**: 다큐멘터리 내레이션 및 해설\n- **E-learning**: 교육 콘텐츠 및 튜토리얼\n- **Rapper**: 랩 및 음악 퍼포먼스\n- **Game**: 비디오 게임 보이스 및 내레이션\n- **Tiktok/Reels**: SNS 숏츠\n- **News**: 뉴스 방송\n- **Podcast**: 방송 및 팟캐스트 제작\n- **Voicemail**: IVR 시스템 및 음성 비서\n- **Ads**: 광고 및 홍보 콘텐츠\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "문제를 설명하는 오류 메시지" }, "message": { "type": "string", "description": "구조화된 오류에 대한 설명" }, "error_code": { "type": "string", "description": "구조화된 오류를 식별하는 코드" } }, "description": "기존 오류와 유효성 검사 오류는 `detail`을 사용하며, 구조화된 오류는 `error_code`와 `message`를 사용합니다." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "`status`가 `failed`일 때의 실패 사유입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "source": { "type": "string", "title": "Source", "description": "생성 방식(`instant` 또는 `professional`)입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "UTC 기준 생성 시각입니다." } }, "description": "인증된 계정이 소유한 커스텀 보이스입니다." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "추천 관련도 점수. 값이 높을수록 검색어와 더 잘 맞는 후보입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "`tc_` prefix 가 붙은 타입캐스트 보이스 식별자. 텍스트 음성 변환 요청의 `voice_id` 로 사용할 수 있습니다." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "사람이 읽을 수 있는 보이스 이름." } }, "description": "`GET /v1/voices/recommendations` 응답의 보이스 추천 후보. 관련도 순서로 정렬됩니다." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "커스텀 보이스의 현재 생성 또는 학습 상태입니다." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름 (1~30자)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "보이스가 클로닝된 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "`uc_` prefix 가 붙은 커스텀 보이스 식별자. `POST /v1/text-to-speech` 등 `voice_id` 를 받는 엔드포인트에 그대로 사용할 수 있습니다." } }, "description": "`POST /v1/voices/clone` 응답 — 퀵 클로닝으로 생성된 커스텀 보이스 메타데이터." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "세그먼트 구분값입니다. 항상 `pause`입니다." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "초 단위 쉼 길이입니다. 개별 쉼은 최대 10초, 모든 쉼의 합은 최대 60초입니다.", "exclusiveMinimum": 0 } }, "description": "크레딧 차감 없이 삽입되는 무음 구간입니다.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 단어 단위 정렬 구간." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "현재 구독 플랜명" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "사용 제한 정보" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "크레딧 사용 정보" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "구독 정보 응답 모델" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호/공백 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 문자 단위 정렬 구간." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." } }, "description": "퀵 클로닝 또는 프리미엄 클로닝 생성 요청 결과입니다." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "base64 로 인코딩된 오디오 바이트. `audio_format` 확장자로 디코딩해 파일로 저장할 수 있습니다." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "단어 단위 타임스탬프(문장부호 포함). 요청이 `granularity=char` 일 때는 `null`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "문자 단위 타임스탬프(문장부호와 공백 포함). 요청이 `granularity=word` 일 때는 `null`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "`audio` 필드의 오디오 인코딩 포맷 — `wav` 또는 `mp3` (요청의 `output.audio_format` 에 따라 결정)." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "생성된 오디오의 길이(초)." } }, "description": "TTS 생성 + 타임스탬프 정렬 통합 응답." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "오디오 샘플. WAV 또는 MP3, 최대 25MB, 5초 이상 150초 이하.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "보이스 이름 (1~30자)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "클로닝할 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." } }, "description": "퀵 클로닝 요청을 위한 multipart 본문." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "파일", "format": "binary", "description": "25 MiB 이하이면서 길이가 5초 이상 150초 이하인 WAV 또는 MP3 음성 파일 1개입니다.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "상품 내레이터", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." } }, "description": "퀵 클로닝용 multipart 요청 본문입니다." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "커스텀 보이스 이름", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "파일", "description": "학습에 사용할 WAV 또는 MP3 음성 파일입니다. 하나의 파일을 업로드할 수 있습니다.\r\n\r\n**업로드 제한**\r\n\r\n* WAV 또는 MP3 파일 1개\r\n* 파일 크기: 1 GiB 이하\r\n* 길이: 5분 이상 3시간 이하\r\n* 샘플레이트: 16 kHz 이상\r\n\r\n최상의 결과를 위해 다음 조건을 충족하는 오디오를 권장합니다.\r\n\r\n* 생성된 보이스가 재현하기를 원하는 말투와 최대한 가깝게 녹음해 주세요.\r\n* 배경 소음이 없는 조용한 환경에서 녹음해 주세요.\r\n* 한 명의 화자 목소리만 포함해 주세요.\r\n* `language` 필드에 지정한 언어로 녹음해 주세요.\r\n* 오디오 길이가 길수록 생성되는 보이스의 품질이 향상됩니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." }, "language": { "type": "string", "title": "Language", "example": "kor", "description": "`kor`, `eng` 같은 ISO 639-3 언어 코드입니다." } }, "description": "프리미엄 클로닝용 multipart 요청 본문입니다." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "인증을 위한 API 키. 타입캐스트 API 콘솔에서 API 키를 생성할 수 있습니다." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 커스텀 보이스 삭제 > 인증된 계정이 소유한 커스텀 보이스를 소프트 삭제하여 슬롯을 비웁니다. 프리미엄 클로닝 보이스가 학습 중이면 보이스 삭제 후 학습도 중단합니다. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "프로덕션 서버" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/custom-voices/{voice_id}": { "delete": { "tags": [ "Custom Voices" ], "x-mint": { "href": "/ko/api-reference/custom-voices/delete-custom-voice" }, "summary": "커스텀 보이스 삭제", "responses": { "204": { "description": "커스텀 보이스를 삭제했습니다." }, "400": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Invalid voice ID", "error_code": "INVALID_VOICE_ID" } } }, "description": "커스텀 보이스 ID 형식이 올바르지 않습니다." }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "인증에 실패했습니다." }, "404": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "message": "Custom voice not found", "error_code": "CUSTOM_VOICE_NOT_FOUND" } } }, "description": "커스텀 보이스가 없거나 삭제되었거나 다른 계정이 소유하고 있습니다." }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid request format" } } }, "description": "유효하지 않은 보이스 ID입니다." } }, "parameters": [ { "in": "path", "name": "voice_id", "schema": { "type": "string", "title": "Voice Id" }, "required": true, "description": "삭제할 커스텀 보이스의 고유 식별자입니다." } ], "description": "인증된 계정이 소유한 커스텀 보이스를 소프트 삭제하여 슬롯을 비웁니다. 프리미엄 클로닝 보이스가 학습 중이면 보이스 삭제 후 학습도 중단합니다.", "operationId": "delete_custom_voice_v1_custom_voices__voice_id__delete", "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request DELETE \\\n --url https://api.typecast.ai/v1/custom-voices/uc_6700000000000000000000bb \\\n --header 'X-API-KEY: '\n" }, { "lang": "Python", "label": "Python (requests)", "source": "import requests\n\nresponse = requests.delete(\n \"https://api.typecast.ai/v1/custom-voices/uc_6700000000000000000000bb\",\n headers={\"X-API-KEY\": \"\"},\n timeout=30,\n)\nresponse.raise_for_status()\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "허용되는 최대 동시 요청 수" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "전체 커스텀 보이스 슬롯 한도 (professional 포함)" }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "사용 제한 정보" }, "Prompt": { "title": "프롬프트 (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "적용할 감정 프리셋.\r\n\r\nssfm-v21 지원 감정: normal, happy, sad, angry\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "example": 1, "description": "감정 표현 강도 제어(0.0~2.0).\r\n\r\n- 0.0: 완전히 중립적\r\n- 1.0: 표준 표현(기본값)\r\n- 2.0: 최대 강도\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "연령대 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **child**: 어린이 보이스(12세 미만)\n- **teenager**: 청소년 보이스(13-19세)\n- **young_adult**: 청년 보이스(20-35세)\n- **middle_age**: 중년 보이스(36-60세)\n- **elder**: 노년 보이스(60세 이상)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "플랜에서 기본으로 제공하는 총 크레딧" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "사용된 크레딧 수" } }, "description": "크레딧 사용 정보" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대 분류(어린이/청소년/청년/중년/노년)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별 분류(남성/여성)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "사용 가능한 감정이 있는 지원되는 TTS 모델 목록(예: [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유한 보이스 식별자. 기본 제공 보이스는 `tc_` prefix (예: `tc_60e5426de8b95f1d3000d7b5`), `POST /v1/voices/clone` 으로 만든 커스텀 보이스는 `uc_` prefix 를 사용합니다. 본인이 소유한 `uc_` 보이스도 `/v2/voices` 응답에 포함됩니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "이 보이스가 적합한 사용 사례 카테고리 목록\n\n## 보이스 이름 참고\n\n`voice_name` 필드는 영문으로만 반환됩니다. 아래 목록에서 한국어 표기를 확인하실 수 있습니다.\n\n
\n보이스 이름 목록 펼치기 (542개)\n\n| 영어 | 한국어 | 영어 | 한국어 | 영어 | 한국어 |\n| --- | --- | --- | --- | --- | --- |\n| Aaron | 에런 | Inhye | 인혜 | Piljae | 필재 |\n| Abigail | 아비게일 | Inseong | 인성 | Pyeonghwa | 평화 |\n| Ae-ran | 애란 | Instructor Han | 한유격 교관 | Rachel | 레이첼 |\n| Ael | 아엘 | Insun | 인선 | Ravi | 라비 |\n| Agatha | 아가사 | Jabbaba | 자바바 | Rayeon | 라연 |\n| Aiden | 에이든 | Jack | 잭 | Rebecca | 레베카 |\n| Alena | 알레나 | Jack-o’-Lantern | 잭오랜턴 | Reporter Bona | 다보나 기자 |\n| Alex | 알렉스 | Jackson | 잭슨 | Reporter Catalina | 카탈리나 기자 |\n| Amber | 엠버 | Jaeho | 재호 | Reporter Jinsub | 송진섭 기자 |\n| Anchor Hwa | 김경화 앵커 | Jaehun | 재훈 | Reporter Kang | 강수정 기자 |\n| Angela | 안젤라 | Jaejun | 재준 | Reporter Lee | 이승주 기자 |\n| Anja | 안냐 | Jaekyung | 재경 | Reporter Parkchan | 박찬 기자 |\n| Annabelle the Ghost | 귀신 애나벨 | Jaesun | 재선 | Rex | 렉스 |\n| Annie | 애니 | JaeYi | 재이 | Riley | 라일리 |\n| April | 에이프릴 | Jaeyoung | 재영 | Risan Ji | 지리산 |\n| Arang | 아랑 | Jain | 자인 | Rita | 리타 |\n| Ari | 아리 | Jake | 제이크 | River | 리버 |\n| Arin | 아린 | Janet | 재닛 | Robert | 로버트 |\n| Athena | 아테나 | Jangho | 장호 | Robo | 로보 |\n| Audrey | 오드리 | Jangwoon | 장운 | Romi | 로미 |\n| Avery | 에이버리 | Jechan | 제찬 | Ron | 론 |\n| Avong | 아봉 | Jeff | 제프 | Roro | 로로 |\n| Azzi | 아찌 | Jenna | 제나 | Rowoon | 로운 |\n| Babilon | 바빌론 | Jennifer | 제니퍼 | Royce | 로이스 |\n| Bell | 벨 | Jeong Choi | 최정 | Rudolph | 루돌프 |\n| Ben | 벤 | Jeongah | 정아 | Ruri | 루리 |\n| Benny | 베니 | JeongJin | 정진 | Rusty | 러스티 |\n| Beri | 베리 | Jeongseob | 정섭 | Ryan | 라이언 |\n| Billie | 빌리 | Jerome | 제롬 | Ryueun | 류은 |\n| Bomi | 이보미 | Jewby | 쥬비 | Sabrina the Witch | 마녀 사브리나 |\n| Bonggyu | 봉규 | Jian | 지안 | Saeron | 새론 |\n| Bongman Kim | 김봉만 | Jicheol | 지철 | Salty Chan-gu | 삐뚤어진 찬구 |\n| Bono | 보노 | Jihee | 지희 | Samantha | 사만다 |\n| Bora | 보라 | Jihoon | 지훈 | Sammy | 새미 |\n| Brad | 브래드 | Jihyun | 지현 | Sangdo | 상도 |\n| Bruce | 브루스 | Jimmy | 지미 | Sanghoon | 상훈 |\n| Buddy | 버디 | Jin | 진 | Sangwoo | 상우 |\n| Bumsoo | 범수 | Jinhan | 진한 | Santa | 산타 |\n| Buttaguy | 빠다가이 | Jinhang Kim | 김진행 | Santa Claus | 산타클로스 |\n| Caitlyn | 케이틀린 | Jinhee | 진희 | Santa Reporter | VJ 싼타 |\n| Callan | 칼란 | Jinhyuk | 진혁 | Sara | 세라 |\n| Camila | 카밀라 | Jinseo | 진서 | Sean | 션 |\n| Captain Bill | 캡틴 빌 | Jinung | 진웅 | SeHee | 세희 |\n| Carl | 칼 | Jinwoo | 진우 | Seheon | 세헌 |\n| Carlos | 카를로스 | Jiwoo | 지우 | Sejin | 세진 |\n| Carol | 캐롤 | Jiyoon | 지윤 | Sena | 세나 |\n| Catherine | 캐서린 | Jiyoung | 지영 | Seohee | 서희 |\n| Chad | 채드 | Jodie | 조디 | Seojin | 서진 |\n| Chaeah | 채아 | Jolly | 졸리 | Seojoon | 서준 |\n| Chan-gu | 찬구 | Jongdae | 종대 | Seok Choi | 최석 |\n| Changbae | 창배 | Jooeun | 주은 | SeokPil | 한석필 |\n| Changhee | 창희 | Joonghyun | 중현 | Seokpyo | 석표 |\n| Changmin | 창민 | Joongsik | 중식 | Seolhwa | 설화 |\n| Changsu | 박창수 | Joonkyu | 준규 | Seonha | 선하 |\n| Chanhyuk | 찬혁 | Joshua | 조슈아 | Seoyeon | 서연 |\n| Charlotte | 샬롯 | Juha | 주하 | Seoyoon | 서윤 |\n| Chase | 체이스 | Juho | 주호 | Seungah | 승아 |\n| Cheolhoon | 철훈 | Jungbong | 정봉 | Seungheon | 승헌 |\n| Cheolyong | 리철용 | Junghee | 정희 | Seungho | 승호 |\n| Cherry | 체리 | Jungjae | 정재 | Seunghwa | 승화 |\n| Chester | 체스터 | Jungmin | 정민 | Seungjae | 승재 |\n| Chiho | 치호 | Jungseok | 정석 | Seungmoon | 승문 |\n| Chloe | 클로이 | Jungsoon | 정순 | Seungwon | 승원 |\n| Choyeon | 강초연 교관 | Jungwon | 정원 | Seungyeon | 승연 |\n| Chungah | 청아 | Junhee | 준희 | Sewoo | 세우 |\n| Chunsik Kang | 강춘식 기자 | Junho | 준호 | Shana | 샤나 |\n| Claire | 클레어 | JunKi | 준기 | Sheriff Kim | 김반장 |\n| Classic Narrator | 미스타 변사 | Junsang | 준상 | Shinhe | 신혜 |\n| Cole | 콜 | Junseong | 준성 | Shinwook | 신욱 |\n| Cox | 콕스 | Justice Roh | 정의로 | Shotgun | 샷건 |\n| Cyrus | 사이러스 | Justin | 저스틴 | Simon | 사이먼 |\n| Dabin | 다빈 | Juwon | 주원 | Sindarin | 신다린 |\n| Daegil | 대길 | Juyoung | 주영 | Sio | 시오 |\n| Dahee | 다희 | K-Santa | 산타 할아버지 | Siwon | 시원 |\n| Dahyeon | 다현 | Kangil | 강일 | Siwoo | 시우 |\n| Damian | 데이미언 | Kanno | 칸노 | Siyeon | 시연 |\n| Dan | 댄 | Katie | 케이티 | Skylar | 스카일러 |\n| Dana | 다나 | Kelly | 켈리 | Slushy | 슬러시 |\n| Dasom | 다솜 | Kelsey | 켈시 | Smoke | 스모크 |\n| David | 데이비드 | Kevin | 케빈 | Sohye | 소혜 |\n| Dean | 딘 | Keybo | 키보 | Sojin | 소진 |\n| Deokhwan | 덕환 | Kijang Kim | 김기장 | Soobin | 수빈 |\n| Dohan | 도한 | Killian the Vampire | 뱀파이어 킬리언 | Sookhee | 숙희 |\n| Dohee | 도희 | Kiseob | 기섭 | Sooni | 순이 |\n| Dohyun | 도현 | Klip Kim | 클립 킴 | Sophia | 소피아 |\n| Dollar Jr. | 달러 주니어 | Koombo | 쿰보 | Sora | 소라 |\n| Doug | 더그 | Kristen | 크리스틴 | Soye | 소예 |\n| Doughnut | 도넛 | Kukhee | 국희 | Soyi | 소이 |\n| DU5T | 더스트 | Kwonil | 권일 | Soyoung | 소영 |\n| Duckchun | 덕춘 | Kyumin | 규민 | Soyul | 소율 |\n| Duke | 듀크 | Kyungho | 경호 | Spice | 스파이스 |\n| Dukgu | 덕구 | Kyungsoo | 경수 | Sportscaster Kang | 강수정 캐스터 |\n| Duman | 두만 | Kyungsook | 경숙 | Sportscaster Tony | 이영광 캐스터 |\n| Dupil | 곽두필 | Lady Cho | 단희빈 | Starling | 스탈링 |\n| DVZY | 데이지 | Lala | 라라 | Stephanie | 스테파니 |\n| Dylan | 딜런 | Lamie | 라미 | Storyteller Jinsub | 송진섭 스토리텔러 |\n| E-seller Sherri | 쇼린이 | Landon | 랜던 | Sua | 수아 |\n| Echo | 에코 | Larry | 래리 | Suho | 수호 |\n| Edward | 에드워드 | Leo | 레오 | Suji | 수지 |\n| Elias | 엘리아스 | Liam | 리암 | Sujin | 수진 |\n| Elise | 엘리스 | Lindsay | 린지 | Sullock Hong | 홍설록 |\n| Ella | 엘라 | Liz | 리즈 | Sumin | 수민 |\n| Eman | 이만 | Lloyd | 로이드 | Sungbae | 성배 |\n| Emma | 엠마 | Logan | 로건 | Sunggyu | 성규 |\n| Eunbin | 은빈 | Lucille | 루실 | Sungho | 성호 |\n| Eunchae | 은채 | Luna | 루나 | Sunghoon | 성훈 |\n| Eunha | 은하 | Lydia | 리디아 | Sunghyun | 성현 |\n| Eunsol | 은솔 | Maddie | 매디 | Sungjun | 홈쇼핑 성준 |\n| Frankenstein | 프랑켄슈타인 | Maisie | 메이시 | Sungkwon | 성권 |\n| Furnando | 터르난도 | Margaret | 마가렛 | Sungtae | 성태 |\n| Gaeul | 가을 | Margot | 마고 | Sungwook | 성욱 |\n| Gahee | 가희 | Matthew | 매튜 | Sunyoung | 선영 |\n| George | 조지 | MBTI EF (F) | MBTI EF 여 | Suyoon | 수윤 |\n| GeumHee | 금희 | MBTI EF (M) | MBTI EF 남 | Sylvia | 실비아 |\n| Geunhyeok | 근혁 | MBTI ET (F) | MBTI ET 여 | Taebaek | 장태백 |\n| Geunseok | 근석 | MBTI ET (M) | MBTI ET 남 | Taeji | 태지 |\n| Geunwoo | 근우 | MBTI IF (F) | MBTI IF 여 | Taejoong | 태중 |\n| Geunyeong | 근영 | MBTI IF (M) | MBTI IF 남 | Taemin | 태민 |\n| Ggami | 까미 | MBTI IT (F) | MBTI IT 여 | Taesub | 태섭 |\n| Ggomi | 꼬미 | MBTI IT (M) | MBTI IT 남 | Taewoo | 태우 |\n| Glenda | 글렌다 | MC Kong | MC콩 | Teal | 틸 |\n| Goat Kim | 김고트 | MC TypeCast | MC 타캐 | Tessa | 테사 |\n| Gongchul | 공철 | Mia | 미아 | Tian | 티안 |\n| Gowoon | 고운 | Michael | 마이클 | Tim | 팀 |\n| Grace | 그레이스 | Mija | 오미자 | Tina | 티나 |\n| Graham | 그레이엄 | Mijin | 미진 | Toby | 토비 |\n| Gunseok | 건석 | Mikyung | 미경 | Tommy | 토미 |\n| Gunwoo | 건우 | Millie | 밀리 | Tyson | 타이슨 |\n| Guri | 구리 | Minchae | 민채 | UiChan | 의찬이 |\n| Gus | 거스 | Minji | 민지 | Uncle Hank | 행크 |\n| Ha Eun | 하은 | Minjoon | 민준 | Valerie | 밸러리 |\n| Haejun | 해준 | Minju | 민주 | Valkyrie | 발키리 |\n| Haerang | 해랑 | Minjung | 민정 | Vanessa | 바네사 |\n| Hailey | 헤일리 | Minsang | 민상 | Verna | 베르나 |\n| Hajun | 하준 | Minsu | 민수 | Victoria | 빅토리아 |\n| Hakchul | 학철 | Mio | 미오 | Viktor | 빅토르 |\n| Hamchu | 햄쮸 | Miran Choi | 최미란 | Vincent | 빈센트 |\n| Han Taesung Caster | 한태성 캐스터 | Mirine | 미리내 | Vivien | 비비안 |\n| Hana | 하나 | Miseon | 미선 | Wade | 웨이드 |\n| Hangyeol | 한결 | Miso | 미소 | Walter | 월터 |\n| Hanjun | 한준 | Mister Gop | 미스터 갑 | Wangkwon | 왕권 |\n| Hanna | 한나 | Monggun | 몽군 | Weather Reporter Sky | 하늘 캐스터 |\n| Hans | 한스 | Moonjung | 문정 | West | 웨스트 |\n| Hansol | 한솔 | Mooyeol | 무열 | Wonho | 원호 |\n| Hanyoung | 한영 | Morgan | 모건 | Wonkyung | 원경 |\n| Harin | 하린 | Moru | 머루 | Wonwoo | 원우 |\n| Harper | 하퍼 | Mrs. Claus | 미세스 클로스 | Wooju | 우주 |\n| Hayul | 하율 | Munseok | 문석 | Woosung | 우성 |\n| Heejoon | 희준 | Munsu | 문수 | Xavier | 자비에르 |\n| Helena | 헬레나 | Myeonghee | 명희 | Yeeun | 예은 |\n| Henry | 헨리 | Myungil | 명일 | Yejin | 예진 |\n| Homun Sim | 심호문 | Myungjoo | 명주 | Yejoon | 예준 |\n| Hoon | 훈 | Naeun | 나은 | YeLin | 예린 |\n| Hosik | 호식 | Najin | 나진 | Yena | 예나 |\n| Hosun | 호선 | Namjoon | 남준 | Yeonah | 연아 |\n| Hoyoung | 호영 | Nana | 나나 | Yeonggeol | 영걸 |\n| Hugh | 휴 | Nari | 개나리 | Yeonhwa | 연화 |\n| Hwimin | 휘민 | Nathan | 네이선 | Yeonja | 연자 |\n| Hyejung | 혜정 | Neel | 닐 | Yeonsuh | 연서 |\n| Hyelee | 혜리 | Neoguard | 네오가드 | Yeonwoo | 연우 |\n| Hyemin | 혜민 | News anchor Jinsub | 송진섭 앵커 | Yeseul | 예슬 |\n| Hyena | 혜나 | Newscaster John | 존 앵커 | Yongsik | 용식이 |\n| Hyeongjin | 형진 | Nia | 니아 | Yoonseo | 윤서 |\n| Hyera | 혜라 | Noa | 노아 | Yoonsung | 윤성 |\n| Hyesu | 홈쇼핑 혜수 | Noel | 노엘 | Younggil | 영길 |\n| Hyewon | 혜원 | Noeul | 노을 | Younghee | 영희 |\n| Hyun | 현 | Norah | 노라 | Younghwan | 영환 |\n| Hyunji | 현지 | Nova | 노바 | Youngji | 영지 |\n| Hyunjin | 현진 | Old radio | 레디오 | Youngkyu | 영규 |\n| Hyunju | 현주 | Oliver | 올리버 | Youngmok | 영목 |\n| Hyunkyung | 현경 | Olivia | 올리비아 | Yubin | 유빈 |\n| Hyunmin | 현민 | Oscar | 오스카 | Yujin | 유진 |\n| Hyunseung | 현승 | Owen | 오언 | Yumi | 유미 |\n| HyunWoo | 현우 | P-0150N | 포이즌 | Yumin | 유민 |\n| Ian | 이안 | Pacang | 파캉 | Yunbin | 윤빈 |\n| Icarus | 이카루스 | Paige | 페이지 | Yunjeong | 윤정 |\n| Igyeom | 이겸 | Pangpang | 팡팡 | Yura | 유라 |\n| Ijun | 이준 | Patricia | 패트리샤 | Yuri | 유리 |\n| Ilho | 일호 | Patrick | 패트릭 | Yuseong | 유성 |\n| Ina | 이나 | Peter | 피터 | Zoey | 조이 |\n| Inhwa | 인화 | Philip | 필립 | | |\n\n
\n" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "사람이 읽을 수 있는 보이스 이름" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입 — `original` 은 타입캐스트가 기본 제공하는 스톡 보이스, `custom` 은 사용자가 업로드/클론한 보이스입니다." } }, "description": "모델별로 그룹화된 감정과 향상된 메타데이터가 있는 V2 보이스 응답 모델" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대입니다." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별(`male` 또는 `female`)입니다." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "지원하는 TTS 모델과 감정 목록입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유 보이스 식별자입니다. 기본 보이스는 `tc_`, 커스텀 보이스는 `uc_`로 시작합니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "추천 사용 사례 목록입니다." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "`{\"eng\": \"Daejin\", \"kor\": \"대진\"}` 같은 다국어 보이스 이름입니다." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입(`original` 또는 `custom`)입니다." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "제공되는 경우 사용할 수 있는 미리듣기 오디오 URL입니다." } }, "description": "다국어 이름과 미리듣기 URL을 포함한 보이스 정보입니다." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "API 플랜.\n\n사용 가능한 값:\n- **free**: 제한된 크레딧의 무료 등급\n- **lite**: 중간 수준 크레딧의 라이트 플랜\n- **plus**: 높은 크레딧의 플러스 플랜\n- **custom**: 커스텀 엔터프라이즈 플랜" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "음성 합성에 사용할 TTS 모델 버전. 다양한 모델은 다양한 기능과 품질 수준을 제공합니다.\n\n사용 가능한 모델:\n- **ssfm-v30**: 향상된 플로우와 추가 감정 프리셋이 있는 최신 모델(권장)\n- **ssfm-v21**: 검증된 신뢰성과 일관된 품질을 갖춘 안정적인 모델\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS 모델 버전(예: ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "이 모델에서 지원되는 감정 목록" } }, "description": "버전 및 지원되는 감정을 포함한 모델 정보" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "영어 보이스 이름입니다." }, "kor": { "type": "string", "title": "Kor", "description": "한국어 보이스 이름입니다." } }, "description": "ISO 639-3 언어 코드를 키로 사용하는 다국어 보이스 이름입니다." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "보이스 타입 분류.\n\n- `original` — 타입캐스트가 기본 제공하는 스톡 보이스로, 모든 계정에서 사용 가능합니다.\n- `custom` — 사용자가 본인의 샘플을 업로드하거나 클로닝해서 만든 보이스입니다." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "성별 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **male**: 남성 보이스\n- **female**: 여성 보이스\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "음성 합성에 사용 가능한 감정 프리셋. 각 감정은 생성된 음성의 톤, 속도, 표현력에 영향을 줍니다.\n\n**ssfm-v21 지원 감정 (4종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n\n**ssfm-v30 지원 감정 (7종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n- whisper: 부드럽고 조용한 말\n- toneup: 더 높은 톤 강조\n- tonedown: 더 낮은 톤 강조\n\n/v2/voices API 응답을 통해 각 음성에 사용 가능한 감정을 확인하세요.\n" }, "SmartPrompt": { "type": "object", "title": "스마트 프롬프트 (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "TTSRequest의 `text` 필드 이후에 오는 텍스트. 감정 추론을 위한 전방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 전환을 예측하는 데 도움\r\n- 다음 컨텍스트가 없으면 비워 둡니다\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "프롬프트 유형을 식별하는 판별자 필드. 컨텍스트 인식 감정 추론을 위해 \"smart\"로 설정해야 합니다.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "TTSRequest의 `text` 필드 이전에 오는 텍스트. 감정 추론을 위한 후방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 빌드업과 컨텍스트를 이해하는 데 도움\r\n- 이전 컨텍스트가 없으면 비워 둡니다\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "프리셋 프롬프트 (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "프롬프트 유형을 식별하는 판별자 필드. 프리셋 기반 감정 제어를 위해 \"preset\"으로 설정해야 합니다.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "생성된 음성에 적용할 감정 프리셋.\r\n\r\n지원되는 감정: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "생성된 음성의 감정 표현 강도를 제어합니다.\r\n\r\n- 0.0: 완전히 중립적, 감정 색채 없음\r\n- 0.5: 미묘한 감정 힌트\r\n- 1.0: 표준 감정 표현(기본값)\r\n- 1.5: 강한 감정 강조\r\n- 2.0: 최대 강도, 매우 표현력 있음\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "콘텐츠 유형 필터링을 위한 보이스 사용 사례 카테고리. 각 보이스는 특정 콘텐츠 유형에 대한 적합성을 나타내는 하나 이상의 사용 사례로 태그됩니다.\n\n**사용 가능한 사용 사례:**\n- **Announcer**: 공공 발표 및 프레젠테이션\n- **Anime**: 애니메이션 보이스\n- **Audiobook**: 장문 내레이션 및 스토리텔링\n- **Conversational**: 챗봇 및 대화형 AI\n- **Documentary**: 다큐멘터리 내레이션 및 해설\n- **E-learning**: 교육 콘텐츠 및 튜토리얼\n- **Rapper**: 랩 및 음악 퍼포먼스\n- **Game**: 비디오 게임 보이스 및 내레이션\n- **Tiktok/Reels**: SNS 숏츠\n- **News**: 뉴스 방송\n- **Podcast**: 방송 및 팟캐스트 제작\n- **Voicemail**: IVR 시스템 및 음성 비서\n- **Ads**: 광고 및 홍보 콘텐츠\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "문제를 설명하는 오류 메시지" }, "message": { "type": "string", "description": "구조화된 오류에 대한 설명" }, "error_code": { "type": "string", "description": "구조화된 오류를 식별하는 코드" } }, "description": "기존 오류와 유효성 검사 오류는 `detail`을 사용하며, 구조화된 오류는 `error_code`와 `message`를 사용합니다." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "`status`가 `failed`일 때의 실패 사유입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "source": { "type": "string", "title": "Source", "description": "생성 방식(`instant` 또는 `professional`)입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "UTC 기준 생성 시각입니다." } }, "description": "인증된 계정이 소유한 커스텀 보이스입니다." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "추천 관련도 점수. 값이 높을수록 검색어와 더 잘 맞는 후보입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "`tc_` prefix 가 붙은 타입캐스트 보이스 식별자. 텍스트 음성 변환 요청의 `voice_id` 로 사용할 수 있습니다." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "사람이 읽을 수 있는 보이스 이름." } }, "description": "`GET /v1/voices/recommendations` 응답의 보이스 추천 후보. 관련도 순서로 정렬됩니다." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "커스텀 보이스의 현재 생성 또는 학습 상태입니다." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름 (1~30자)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "보이스가 클로닝된 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "`uc_` prefix 가 붙은 커스텀 보이스 식별자. `POST /v1/text-to-speech` 등 `voice_id` 를 받는 엔드포인트에 그대로 사용할 수 있습니다." } }, "description": "`POST /v1/voices/clone` 응답 — 퀵 클로닝으로 생성된 커스텀 보이스 메타데이터." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "세그먼트 구분값입니다. 항상 `pause`입니다." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "초 단위 쉼 길이입니다. 개별 쉼은 최대 10초, 모든 쉼의 합은 최대 60초입니다.", "exclusiveMinimum": 0 } }, "description": "크레딧 차감 없이 삽입되는 무음 구간입니다.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 단어 단위 정렬 구간." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "현재 구독 플랜명" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "사용 제한 정보" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "크레딧 사용 정보" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "구독 정보 응답 모델" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호/공백 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 문자 단위 정렬 구간." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." } }, "description": "퀵 클로닝 또는 프리미엄 클로닝 생성 요청 결과입니다." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "base64 로 인코딩된 오디오 바이트. `audio_format` 확장자로 디코딩해 파일로 저장할 수 있습니다." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "단어 단위 타임스탬프(문장부호 포함). 요청이 `granularity=char` 일 때는 `null`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "문자 단위 타임스탬프(문장부호와 공백 포함). 요청이 `granularity=word` 일 때는 `null`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "`audio` 필드의 오디오 인코딩 포맷 — `wav` 또는 `mp3` (요청의 `output.audio_format` 에 따라 결정)." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "생성된 오디오의 길이(초)." } }, "description": "TTS 생성 + 타임스탬프 정렬 통합 응답." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "오디오 샘플. WAV 또는 MP3, 최대 25MB, 5초 이상 150초 이하.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "보이스 이름 (1~30자)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "클로닝할 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." } }, "description": "퀵 클로닝 요청을 위한 multipart 본문." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "파일", "format": "binary", "description": "25 MiB 이하이면서 길이가 5초 이상 150초 이하인 WAV 또는 MP3 음성 파일 1개입니다.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "상품 내레이터", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." } }, "description": "퀵 클로닝용 multipart 요청 본문입니다." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "커스텀 보이스 이름", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "파일", "description": "학습에 사용할 WAV 또는 MP3 음성 파일입니다. 하나의 파일을 업로드할 수 있습니다.\r\n\r\n**업로드 제한**\r\n\r\n* WAV 또는 MP3 파일 1개\r\n* 파일 크기: 1 GiB 이하\r\n* 길이: 5분 이상 3시간 이하\r\n* 샘플레이트: 16 kHz 이상\r\n\r\n최상의 결과를 위해 다음 조건을 충족하는 오디오를 권장합니다.\r\n\r\n* 생성된 보이스가 재현하기를 원하는 말투와 최대한 가깝게 녹음해 주세요.\r\n* 배경 소음이 없는 조용한 환경에서 녹음해 주세요.\r\n* 한 명의 화자 목소리만 포함해 주세요.\r\n* `language` 필드에 지정한 언어로 녹음해 주세요.\r\n* 오디오 길이가 길수록 생성되는 보이스의 품질이 향상됩니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." }, "language": { "type": "string", "title": "Language", "example": "kor", "description": "`kor`, `eng` 같은 ISO 639-3 언어 코드입니다." } }, "description": "프리미엄 클로닝용 multipart 요청 본문입니다." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "인증을 위한 API 키. 타입캐스트 API 콘솔에서 API 키를 생성할 수 있습니다." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 구독 정보 조회 > 인증된 사용자의 현재 구독 정보를 조회합니다. 구독 중인 플랜명, 크레딧 사용량, 동시 호출 제한, 커스텀 보이스 슬롯 보유 현황을 포함합니다. TTS 요청 전 남은 크레딧이나 현재 플랜을 확인하거나, `POST /v1/voices/clone` 으로 새 커스텀 보이스를 만들기 전 사용 가능한 슬롯 수를 확인하는 데 사용하세요. `limits.custom_voice_slot` 값은 현재 플랜이 동시에 보유할 수 있는 커스텀 보이스 최대 개수이며, 슬롯이 가득 찼다면 `DELETE /v1/voices/{voice_id}` 로 비워주세요. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "프로덕션 서버" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/users/me/subscription": { "get": { "tags": [ "Subscription" ], "x-mint": { "href": "/ko/api-reference/subscription/get-subscription" }, "summary": "구독 정보 조회", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SubscriptionResponse" }, "example": { "plan": "lite", "limits": { "concurrency_limit": 5, "custom_voice_slot": 10 }, "credits": { "plan_credits": 200000, "used_credits": 157300 } } } }, "description": "Successful Response" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Unauthorized - Authentication failed" }, "429": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Too many requests" } } }, "description": "Too Many Requests - Rate limit exceeded" }, "500": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "An unexpected error occurred" } } }, "description": "Internal Server Error" } }, "description": "인증된 사용자의 현재 구독 정보를 조회합니다. 구독 중인 플랜명, 크레딧 사용량, 동시 호출 제한, 커스텀 보이스 슬롯 보유 현황을 포함합니다.\n\nTTS 요청 전 남은 크레딧이나 현재 플랜을 확인하거나, `POST /v1/voices/clone` 으로 새 커스텀 보이스를 만들기 전 사용 가능한 슬롯 수를 확인하는 데 사용하세요. `limits.custom_voice_slot` 값은 현재 플랜이 동시에 보유할 수 있는 커스텀 보이스 최대 개수이며, 슬롯이 가득 찼다면 `DELETE /v1/voices/{voice_id}` 로 비워주세요.", "operationId": "get_my_subscription_v1_users_me_subscription_get" } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "허용되는 최대 동시 요청 수" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "전체 커스텀 보이스 슬롯 한도 (professional 포함)" }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "사용 제한 정보" }, "Prompt": { "title": "프롬프트 (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "적용할 감정 프리셋.\r\n\r\nssfm-v21 지원 감정: normal, happy, sad, angry\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "example": 1, "description": "감정 표현 강도 제어(0.0~2.0).\r\n\r\n- 0.0: 완전히 중립적\r\n- 1.0: 표준 표현(기본값)\r\n- 2.0: 최대 강도\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "연령대 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **child**: 어린이 보이스(12세 미만)\n- **teenager**: 청소년 보이스(13-19세)\n- **young_adult**: 청년 보이스(20-35세)\n- **middle_age**: 중년 보이스(36-60세)\n- **elder**: 노년 보이스(60세 이상)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "플랜에서 기본으로 제공하는 총 크레딧" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "사용된 크레딧 수" } }, "description": "크레딧 사용 정보" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대 분류(어린이/청소년/청년/중년/노년)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별 분류(남성/여성)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "사용 가능한 감정이 있는 지원되는 TTS 모델 목록(예: [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유한 보이스 식별자. 기본 제공 보이스는 `tc_` prefix (예: `tc_60e5426de8b95f1d3000d7b5`), `POST /v1/voices/clone` 으로 만든 커스텀 보이스는 `uc_` prefix 를 사용합니다. 본인이 소유한 `uc_` 보이스도 `/v2/voices` 응답에 포함됩니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "이 보이스가 적합한 사용 사례 카테고리 목록\n\n## 보이스 이름 참고\n\n`voice_name` 필드는 영문으로만 반환됩니다. 아래 목록에서 한국어 표기를 확인하실 수 있습니다.\n\n
\n보이스 이름 목록 펼치기 (542개)\n\n| 영어 | 한국어 | 영어 | 한국어 | 영어 | 한국어 |\n| --- | --- | --- | --- | --- | --- |\n| Aaron | 에런 | Inhye | 인혜 | Piljae | 필재 |\n| Abigail | 아비게일 | Inseong | 인성 | Pyeonghwa | 평화 |\n| Ae-ran | 애란 | Instructor Han | 한유격 교관 | Rachel | 레이첼 |\n| Ael | 아엘 | Insun | 인선 | Ravi | 라비 |\n| Agatha | 아가사 | Jabbaba | 자바바 | Rayeon | 라연 |\n| Aiden | 에이든 | Jack | 잭 | Rebecca | 레베카 |\n| Alena | 알레나 | Jack-o’-Lantern | 잭오랜턴 | Reporter Bona | 다보나 기자 |\n| Alex | 알렉스 | Jackson | 잭슨 | Reporter Catalina | 카탈리나 기자 |\n| Amber | 엠버 | Jaeho | 재호 | Reporter Jinsub | 송진섭 기자 |\n| Anchor Hwa | 김경화 앵커 | Jaehun | 재훈 | Reporter Kang | 강수정 기자 |\n| Angela | 안젤라 | Jaejun | 재준 | Reporter Lee | 이승주 기자 |\n| Anja | 안냐 | Jaekyung | 재경 | Reporter Parkchan | 박찬 기자 |\n| Annabelle the Ghost | 귀신 애나벨 | Jaesun | 재선 | Rex | 렉스 |\n| Annie | 애니 | JaeYi | 재이 | Riley | 라일리 |\n| April | 에이프릴 | Jaeyoung | 재영 | Risan Ji | 지리산 |\n| Arang | 아랑 | Jain | 자인 | Rita | 리타 |\n| Ari | 아리 | Jake | 제이크 | River | 리버 |\n| Arin | 아린 | Janet | 재닛 | Robert | 로버트 |\n| Athena | 아테나 | Jangho | 장호 | Robo | 로보 |\n| Audrey | 오드리 | Jangwoon | 장운 | Romi | 로미 |\n| Avery | 에이버리 | Jechan | 제찬 | Ron | 론 |\n| Avong | 아봉 | Jeff | 제프 | Roro | 로로 |\n| Azzi | 아찌 | Jenna | 제나 | Rowoon | 로운 |\n| Babilon | 바빌론 | Jennifer | 제니퍼 | Royce | 로이스 |\n| Bell | 벨 | Jeong Choi | 최정 | Rudolph | 루돌프 |\n| Ben | 벤 | Jeongah | 정아 | Ruri | 루리 |\n| Benny | 베니 | JeongJin | 정진 | Rusty | 러스티 |\n| Beri | 베리 | Jeongseob | 정섭 | Ryan | 라이언 |\n| Billie | 빌리 | Jerome | 제롬 | Ryueun | 류은 |\n| Bomi | 이보미 | Jewby | 쥬비 | Sabrina the Witch | 마녀 사브리나 |\n| Bonggyu | 봉규 | Jian | 지안 | Saeron | 새론 |\n| Bongman Kim | 김봉만 | Jicheol | 지철 | Salty Chan-gu | 삐뚤어진 찬구 |\n| Bono | 보노 | Jihee | 지희 | Samantha | 사만다 |\n| Bora | 보라 | Jihoon | 지훈 | Sammy | 새미 |\n| Brad | 브래드 | Jihyun | 지현 | Sangdo | 상도 |\n| Bruce | 브루스 | Jimmy | 지미 | Sanghoon | 상훈 |\n| Buddy | 버디 | Jin | 진 | Sangwoo | 상우 |\n| Bumsoo | 범수 | Jinhan | 진한 | Santa | 산타 |\n| Buttaguy | 빠다가이 | Jinhang Kim | 김진행 | Santa Claus | 산타클로스 |\n| Caitlyn | 케이틀린 | Jinhee | 진희 | Santa Reporter | VJ 싼타 |\n| Callan | 칼란 | Jinhyuk | 진혁 | Sara | 세라 |\n| Camila | 카밀라 | Jinseo | 진서 | Sean | 션 |\n| Captain Bill | 캡틴 빌 | Jinung | 진웅 | SeHee | 세희 |\n| Carl | 칼 | Jinwoo | 진우 | Seheon | 세헌 |\n| Carlos | 카를로스 | Jiwoo | 지우 | Sejin | 세진 |\n| Carol | 캐롤 | Jiyoon | 지윤 | Sena | 세나 |\n| Catherine | 캐서린 | Jiyoung | 지영 | Seohee | 서희 |\n| Chad | 채드 | Jodie | 조디 | Seojin | 서진 |\n| Chaeah | 채아 | Jolly | 졸리 | Seojoon | 서준 |\n| Chan-gu | 찬구 | Jongdae | 종대 | Seok Choi | 최석 |\n| Changbae | 창배 | Jooeun | 주은 | SeokPil | 한석필 |\n| Changhee | 창희 | Joonghyun | 중현 | Seokpyo | 석표 |\n| Changmin | 창민 | Joongsik | 중식 | Seolhwa | 설화 |\n| Changsu | 박창수 | Joonkyu | 준규 | Seonha | 선하 |\n| Chanhyuk | 찬혁 | Joshua | 조슈아 | Seoyeon | 서연 |\n| Charlotte | 샬롯 | Juha | 주하 | Seoyoon | 서윤 |\n| Chase | 체이스 | Juho | 주호 | Seungah | 승아 |\n| Cheolhoon | 철훈 | Jungbong | 정봉 | Seungheon | 승헌 |\n| Cheolyong | 리철용 | Junghee | 정희 | Seungho | 승호 |\n| Cherry | 체리 | Jungjae | 정재 | Seunghwa | 승화 |\n| Chester | 체스터 | Jungmin | 정민 | Seungjae | 승재 |\n| Chiho | 치호 | Jungseok | 정석 | Seungmoon | 승문 |\n| Chloe | 클로이 | Jungsoon | 정순 | Seungwon | 승원 |\n| Choyeon | 강초연 교관 | Jungwon | 정원 | Seungyeon | 승연 |\n| Chungah | 청아 | Junhee | 준희 | Sewoo | 세우 |\n| Chunsik Kang | 강춘식 기자 | Junho | 준호 | Shana | 샤나 |\n| Claire | 클레어 | JunKi | 준기 | Sheriff Kim | 김반장 |\n| Classic Narrator | 미스타 변사 | Junsang | 준상 | Shinhe | 신혜 |\n| Cole | 콜 | Junseong | 준성 | Shinwook | 신욱 |\n| Cox | 콕스 | Justice Roh | 정의로 | Shotgun | 샷건 |\n| Cyrus | 사이러스 | Justin | 저스틴 | Simon | 사이먼 |\n| Dabin | 다빈 | Juwon | 주원 | Sindarin | 신다린 |\n| Daegil | 대길 | Juyoung | 주영 | Sio | 시오 |\n| Dahee | 다희 | K-Santa | 산타 할아버지 | Siwon | 시원 |\n| Dahyeon | 다현 | Kangil | 강일 | Siwoo | 시우 |\n| Damian | 데이미언 | Kanno | 칸노 | Siyeon | 시연 |\n| Dan | 댄 | Katie | 케이티 | Skylar | 스카일러 |\n| Dana | 다나 | Kelly | 켈리 | Slushy | 슬러시 |\n| Dasom | 다솜 | Kelsey | 켈시 | Smoke | 스모크 |\n| David | 데이비드 | Kevin | 케빈 | Sohye | 소혜 |\n| Dean | 딘 | Keybo | 키보 | Sojin | 소진 |\n| Deokhwan | 덕환 | Kijang Kim | 김기장 | Soobin | 수빈 |\n| Dohan | 도한 | Killian the Vampire | 뱀파이어 킬리언 | Sookhee | 숙희 |\n| Dohee | 도희 | Kiseob | 기섭 | Sooni | 순이 |\n| Dohyun | 도현 | Klip Kim | 클립 킴 | Sophia | 소피아 |\n| Dollar Jr. | 달러 주니어 | Koombo | 쿰보 | Sora | 소라 |\n| Doug | 더그 | Kristen | 크리스틴 | Soye | 소예 |\n| Doughnut | 도넛 | Kukhee | 국희 | Soyi | 소이 |\n| DU5T | 더스트 | Kwonil | 권일 | Soyoung | 소영 |\n| Duckchun | 덕춘 | Kyumin | 규민 | Soyul | 소율 |\n| Duke | 듀크 | Kyungho | 경호 | Spice | 스파이스 |\n| Dukgu | 덕구 | Kyungsoo | 경수 | Sportscaster Kang | 강수정 캐스터 |\n| Duman | 두만 | Kyungsook | 경숙 | Sportscaster Tony | 이영광 캐스터 |\n| Dupil | 곽두필 | Lady Cho | 단희빈 | Starling | 스탈링 |\n| DVZY | 데이지 | Lala | 라라 | Stephanie | 스테파니 |\n| Dylan | 딜런 | Lamie | 라미 | Storyteller Jinsub | 송진섭 스토리텔러 |\n| E-seller Sherri | 쇼린이 | Landon | 랜던 | Sua | 수아 |\n| Echo | 에코 | Larry | 래리 | Suho | 수호 |\n| Edward | 에드워드 | Leo | 레오 | Suji | 수지 |\n| Elias | 엘리아스 | Liam | 리암 | Sujin | 수진 |\n| Elise | 엘리스 | Lindsay | 린지 | Sullock Hong | 홍설록 |\n| Ella | 엘라 | Liz | 리즈 | Sumin | 수민 |\n| Eman | 이만 | Lloyd | 로이드 | Sungbae | 성배 |\n| Emma | 엠마 | Logan | 로건 | Sunggyu | 성규 |\n| Eunbin | 은빈 | Lucille | 루실 | Sungho | 성호 |\n| Eunchae | 은채 | Luna | 루나 | Sunghoon | 성훈 |\n| Eunha | 은하 | Lydia | 리디아 | Sunghyun | 성현 |\n| Eunsol | 은솔 | Maddie | 매디 | Sungjun | 홈쇼핑 성준 |\n| Frankenstein | 프랑켄슈타인 | Maisie | 메이시 | Sungkwon | 성권 |\n| Furnando | 터르난도 | Margaret | 마가렛 | Sungtae | 성태 |\n| Gaeul | 가을 | Margot | 마고 | Sungwook | 성욱 |\n| Gahee | 가희 | Matthew | 매튜 | Sunyoung | 선영 |\n| George | 조지 | MBTI EF (F) | MBTI EF 여 | Suyoon | 수윤 |\n| GeumHee | 금희 | MBTI EF (M) | MBTI EF 남 | Sylvia | 실비아 |\n| Geunhyeok | 근혁 | MBTI ET (F) | MBTI ET 여 | Taebaek | 장태백 |\n| Geunseok | 근석 | MBTI ET (M) | MBTI ET 남 | Taeji | 태지 |\n| Geunwoo | 근우 | MBTI IF (F) | MBTI IF 여 | Taejoong | 태중 |\n| Geunyeong | 근영 | MBTI IF (M) | MBTI IF 남 | Taemin | 태민 |\n| Ggami | 까미 | MBTI IT (F) | MBTI IT 여 | Taesub | 태섭 |\n| Ggomi | 꼬미 | MBTI IT (M) | MBTI IT 남 | Taewoo | 태우 |\n| Glenda | 글렌다 | MC Kong | MC콩 | Teal | 틸 |\n| Goat Kim | 김고트 | MC TypeCast | MC 타캐 | Tessa | 테사 |\n| Gongchul | 공철 | Mia | 미아 | Tian | 티안 |\n| Gowoon | 고운 | Michael | 마이클 | Tim | 팀 |\n| Grace | 그레이스 | Mija | 오미자 | Tina | 티나 |\n| Graham | 그레이엄 | Mijin | 미진 | Toby | 토비 |\n| Gunseok | 건석 | Mikyung | 미경 | Tommy | 토미 |\n| Gunwoo | 건우 | Millie | 밀리 | Tyson | 타이슨 |\n| Guri | 구리 | Minchae | 민채 | UiChan | 의찬이 |\n| Gus | 거스 | Minji | 민지 | Uncle Hank | 행크 |\n| Ha Eun | 하은 | Minjoon | 민준 | Valerie | 밸러리 |\n| Haejun | 해준 | Minju | 민주 | Valkyrie | 발키리 |\n| Haerang | 해랑 | Minjung | 민정 | Vanessa | 바네사 |\n| Hailey | 헤일리 | Minsang | 민상 | Verna | 베르나 |\n| Hajun | 하준 | Minsu | 민수 | Victoria | 빅토리아 |\n| Hakchul | 학철 | Mio | 미오 | Viktor | 빅토르 |\n| Hamchu | 햄쮸 | Miran Choi | 최미란 | Vincent | 빈센트 |\n| Han Taesung Caster | 한태성 캐스터 | Mirine | 미리내 | Vivien | 비비안 |\n| Hana | 하나 | Miseon | 미선 | Wade | 웨이드 |\n| Hangyeol | 한결 | Miso | 미소 | Walter | 월터 |\n| Hanjun | 한준 | Mister Gop | 미스터 갑 | Wangkwon | 왕권 |\n| Hanna | 한나 | Monggun | 몽군 | Weather Reporter Sky | 하늘 캐스터 |\n| Hans | 한스 | Moonjung | 문정 | West | 웨스트 |\n| Hansol | 한솔 | Mooyeol | 무열 | Wonho | 원호 |\n| Hanyoung | 한영 | Morgan | 모건 | Wonkyung | 원경 |\n| Harin | 하린 | Moru | 머루 | Wonwoo | 원우 |\n| Harper | 하퍼 | Mrs. Claus | 미세스 클로스 | Wooju | 우주 |\n| Hayul | 하율 | Munseok | 문석 | Woosung | 우성 |\n| Heejoon | 희준 | Munsu | 문수 | Xavier | 자비에르 |\n| Helena | 헬레나 | Myeonghee | 명희 | Yeeun | 예은 |\n| Henry | 헨리 | Myungil | 명일 | Yejin | 예진 |\n| Homun Sim | 심호문 | Myungjoo | 명주 | Yejoon | 예준 |\n| Hoon | 훈 | Naeun | 나은 | YeLin | 예린 |\n| Hosik | 호식 | Najin | 나진 | Yena | 예나 |\n| Hosun | 호선 | Namjoon | 남준 | Yeonah | 연아 |\n| Hoyoung | 호영 | Nana | 나나 | Yeonggeol | 영걸 |\n| Hugh | 휴 | Nari | 개나리 | Yeonhwa | 연화 |\n| Hwimin | 휘민 | Nathan | 네이선 | Yeonja | 연자 |\n| Hyejung | 혜정 | Neel | 닐 | Yeonsuh | 연서 |\n| Hyelee | 혜리 | Neoguard | 네오가드 | Yeonwoo | 연우 |\n| Hyemin | 혜민 | News anchor Jinsub | 송진섭 앵커 | Yeseul | 예슬 |\n| Hyena | 혜나 | Newscaster John | 존 앵커 | Yongsik | 용식이 |\n| Hyeongjin | 형진 | Nia | 니아 | Yoonseo | 윤서 |\n| Hyera | 혜라 | Noa | 노아 | Yoonsung | 윤성 |\n| Hyesu | 홈쇼핑 혜수 | Noel | 노엘 | Younggil | 영길 |\n| Hyewon | 혜원 | Noeul | 노을 | Younghee | 영희 |\n| Hyun | 현 | Norah | 노라 | Younghwan | 영환 |\n| Hyunji | 현지 | Nova | 노바 | Youngji | 영지 |\n| Hyunjin | 현진 | Old radio | 레디오 | Youngkyu | 영규 |\n| Hyunju | 현주 | Oliver | 올리버 | Youngmok | 영목 |\n| Hyunkyung | 현경 | Olivia | 올리비아 | Yubin | 유빈 |\n| Hyunmin | 현민 | Oscar | 오스카 | Yujin | 유진 |\n| Hyunseung | 현승 | Owen | 오언 | Yumi | 유미 |\n| HyunWoo | 현우 | P-0150N | 포이즌 | Yumin | 유민 |\n| Ian | 이안 | Pacang | 파캉 | Yunbin | 윤빈 |\n| Icarus | 이카루스 | Paige | 페이지 | Yunjeong | 윤정 |\n| Igyeom | 이겸 | Pangpang | 팡팡 | Yura | 유라 |\n| Ijun | 이준 | Patricia | 패트리샤 | Yuri | 유리 |\n| Ilho | 일호 | Patrick | 패트릭 | Yuseong | 유성 |\n| Ina | 이나 | Peter | 피터 | Zoey | 조이 |\n| Inhwa | 인화 | Philip | 필립 | | |\n\n
\n" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "사람이 읽을 수 있는 보이스 이름" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입 — `original` 은 타입캐스트가 기본 제공하는 스톡 보이스, `custom` 은 사용자가 업로드/클론한 보이스입니다." } }, "description": "모델별로 그룹화된 감정과 향상된 메타데이터가 있는 V2 보이스 응답 모델" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대입니다." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별(`male` 또는 `female`)입니다." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "지원하는 TTS 모델과 감정 목록입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유 보이스 식별자입니다. 기본 보이스는 `tc_`, 커스텀 보이스는 `uc_`로 시작합니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "추천 사용 사례 목록입니다." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "`{\"eng\": \"Daejin\", \"kor\": \"대진\"}` 같은 다국어 보이스 이름입니다." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입(`original` 또는 `custom`)입니다." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "제공되는 경우 사용할 수 있는 미리듣기 오디오 URL입니다." } }, "description": "다국어 이름과 미리듣기 URL을 포함한 보이스 정보입니다." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "API 플랜.\n\n사용 가능한 값:\n- **free**: 제한된 크레딧의 무료 등급\n- **lite**: 중간 수준 크레딧의 라이트 플랜\n- **plus**: 높은 크레딧의 플러스 플랜\n- **custom**: 커스텀 엔터프라이즈 플랜" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "음성 합성에 사용할 TTS 모델 버전. 다양한 모델은 다양한 기능과 품질 수준을 제공합니다.\n\n사용 가능한 모델:\n- **ssfm-v30**: 향상된 플로우와 추가 감정 프리셋이 있는 최신 모델(권장)\n- **ssfm-v21**: 검증된 신뢰성과 일관된 품질을 갖춘 안정적인 모델\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS 모델 버전(예: ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "이 모델에서 지원되는 감정 목록" } }, "description": "버전 및 지원되는 감정을 포함한 모델 정보" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "영어 보이스 이름입니다." }, "kor": { "type": "string", "title": "Kor", "description": "한국어 보이스 이름입니다." } }, "description": "ISO 639-3 언어 코드를 키로 사용하는 다국어 보이스 이름입니다." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "보이스 타입 분류.\n\n- `original` — 타입캐스트가 기본 제공하는 스톡 보이스로, 모든 계정에서 사용 가능합니다.\n- `custom` — 사용자가 본인의 샘플을 업로드하거나 클로닝해서 만든 보이스입니다." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "성별 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **male**: 남성 보이스\n- **female**: 여성 보이스\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "음성 합성에 사용 가능한 감정 프리셋. 각 감정은 생성된 음성의 톤, 속도, 표현력에 영향을 줍니다.\n\n**ssfm-v21 지원 감정 (4종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n\n**ssfm-v30 지원 감정 (7종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n- whisper: 부드럽고 조용한 말\n- toneup: 더 높은 톤 강조\n- tonedown: 더 낮은 톤 강조\n\n/v2/voices API 응답을 통해 각 음성에 사용 가능한 감정을 확인하세요.\n" }, "SmartPrompt": { "type": "object", "title": "스마트 프롬프트 (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "TTSRequest의 `text` 필드 이후에 오는 텍스트. 감정 추론을 위한 전방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 전환을 예측하는 데 도움\r\n- 다음 컨텍스트가 없으면 비워 둡니다\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "프롬프트 유형을 식별하는 판별자 필드. 컨텍스트 인식 감정 추론을 위해 \"smart\"로 설정해야 합니다.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "TTSRequest의 `text` 필드 이전에 오는 텍스트. 감정 추론을 위한 후방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 빌드업과 컨텍스트를 이해하는 데 도움\r\n- 이전 컨텍스트가 없으면 비워 둡니다\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "프리셋 프롬프트 (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "프롬프트 유형을 식별하는 판별자 필드. 프리셋 기반 감정 제어를 위해 \"preset\"으로 설정해야 합니다.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "생성된 음성에 적용할 감정 프리셋.\r\n\r\n지원되는 감정: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "생성된 음성의 감정 표현 강도를 제어합니다.\r\n\r\n- 0.0: 완전히 중립적, 감정 색채 없음\r\n- 0.5: 미묘한 감정 힌트\r\n- 1.0: 표준 감정 표현(기본값)\r\n- 1.5: 강한 감정 강조\r\n- 2.0: 최대 강도, 매우 표현력 있음\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "콘텐츠 유형 필터링을 위한 보이스 사용 사례 카테고리. 각 보이스는 특정 콘텐츠 유형에 대한 적합성을 나타내는 하나 이상의 사용 사례로 태그됩니다.\n\n**사용 가능한 사용 사례:**\n- **Announcer**: 공공 발표 및 프레젠테이션\n- **Anime**: 애니메이션 보이스\n- **Audiobook**: 장문 내레이션 및 스토리텔링\n- **Conversational**: 챗봇 및 대화형 AI\n- **Documentary**: 다큐멘터리 내레이션 및 해설\n- **E-learning**: 교육 콘텐츠 및 튜토리얼\n- **Rapper**: 랩 및 음악 퍼포먼스\n- **Game**: 비디오 게임 보이스 및 내레이션\n- **Tiktok/Reels**: SNS 숏츠\n- **News**: 뉴스 방송\n- **Podcast**: 방송 및 팟캐스트 제작\n- **Voicemail**: IVR 시스템 및 음성 비서\n- **Ads**: 광고 및 홍보 콘텐츠\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "문제를 설명하는 오류 메시지" }, "message": { "type": "string", "description": "구조화된 오류에 대한 설명" }, "error_code": { "type": "string", "description": "구조화된 오류를 식별하는 코드" } }, "description": "기존 오류와 유효성 검사 오류는 `detail`을 사용하며, 구조화된 오류는 `error_code`와 `message`를 사용합니다." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "`status`가 `failed`일 때의 실패 사유입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "source": { "type": "string", "title": "Source", "description": "생성 방식(`instant` 또는 `professional`)입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "UTC 기준 생성 시각입니다." } }, "description": "인증된 계정이 소유한 커스텀 보이스입니다." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "추천 관련도 점수. 값이 높을수록 검색어와 더 잘 맞는 후보입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "`tc_` prefix 가 붙은 타입캐스트 보이스 식별자. 텍스트 음성 변환 요청의 `voice_id` 로 사용할 수 있습니다." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "사람이 읽을 수 있는 보이스 이름." } }, "description": "`GET /v1/voices/recommendations` 응답의 보이스 추천 후보. 관련도 순서로 정렬됩니다." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "커스텀 보이스의 현재 생성 또는 학습 상태입니다." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름 (1~30자)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "보이스가 클로닝된 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "`uc_` prefix 가 붙은 커스텀 보이스 식별자. `POST /v1/text-to-speech` 등 `voice_id` 를 받는 엔드포인트에 그대로 사용할 수 있습니다." } }, "description": "`POST /v1/voices/clone` 응답 — 퀵 클로닝으로 생성된 커스텀 보이스 메타데이터." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "세그먼트 구분값입니다. 항상 `pause`입니다." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "초 단위 쉼 길이입니다. 개별 쉼은 최대 10초, 모든 쉼의 합은 최대 60초입니다.", "exclusiveMinimum": 0 } }, "description": "크레딧 차감 없이 삽입되는 무음 구간입니다.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 단어 단위 정렬 구간." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "현재 구독 플랜명" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "사용 제한 정보" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "크레딧 사용 정보" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "구독 정보 응답 모델" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호/공백 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 문자 단위 정렬 구간." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." } }, "description": "퀵 클로닝 또는 프리미엄 클로닝 생성 요청 결과입니다." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "base64 로 인코딩된 오디오 바이트. `audio_format` 확장자로 디코딩해 파일로 저장할 수 있습니다." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "단어 단위 타임스탬프(문장부호 포함). 요청이 `granularity=char` 일 때는 `null`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "문자 단위 타임스탬프(문장부호와 공백 포함). 요청이 `granularity=word` 일 때는 `null`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "`audio` 필드의 오디오 인코딩 포맷 — `wav` 또는 `mp3` (요청의 `output.audio_format` 에 따라 결정)." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "생성된 오디오의 길이(초)." } }, "description": "TTS 생성 + 타임스탬프 정렬 통합 응답." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "오디오 샘플. WAV 또는 MP3, 최대 25MB, 5초 이상 150초 이하.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "보이스 이름 (1~30자)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "클로닝할 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." } }, "description": "퀵 클로닝 요청을 위한 multipart 본문." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "파일", "format": "binary", "description": "25 MiB 이하이면서 길이가 5초 이상 150초 이하인 WAV 또는 MP3 음성 파일 1개입니다.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "상품 내레이터", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." } }, "description": "퀵 클로닝용 multipart 요청 본문입니다." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "커스텀 보이스 이름", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "파일", "description": "학습에 사용할 WAV 또는 MP3 음성 파일입니다. 하나의 파일을 업로드할 수 있습니다.\r\n\r\n**업로드 제한**\r\n\r\n* WAV 또는 MP3 파일 1개\r\n* 파일 크기: 1 GiB 이하\r\n* 길이: 5분 이상 3시간 이하\r\n* 샘플레이트: 16 kHz 이상\r\n\r\n최상의 결과를 위해 다음 조건을 충족하는 오디오를 권장합니다.\r\n\r\n* 생성된 보이스가 재현하기를 원하는 말투와 최대한 가깝게 녹음해 주세요.\r\n* 배경 소음이 없는 조용한 환경에서 녹음해 주세요.\r\n* 한 명의 화자 목소리만 포함해 주세요.\r\n* `language` 필드에 지정한 언어로 녹음해 주세요.\r\n* 오디오 길이가 길수록 생성되는 보이스의 품질이 향상됩니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." }, "language": { "type": "string", "title": "Language", "example": "kor", "description": "`kor`, `eng` 같은 ISO 639-3 언어 코드입니다." } }, "description": "프리미엄 클로닝용 multipart 요청 본문입니다." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "인증을 위한 API 키. 타입캐스트 API 콘솔에서 API 키를 생성할 수 있습니다." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 보이스 목록 조회 (v2) > 향상된 메타데이터 및 필터링 기능을 갖춘 사용 가능한 모든 보이스를 조회합니다(V2). 이 엔드포인트는 모델별로 그룹화된 감정 지원과 성별, 연령대, 사용 사례를 포함한 추가 메타데이터가 있는 향상된 보이스 목록을 반환합니다. 각 보이스는 해당 감정 세트와 함께 여러 모델을 지원할 수 있습니다. **주요 기능:** - **모델 그룹화**: 각 보이스에는 지원되는 모든 TTS 모델과 사용 가능한 감정을 보여주는 `models` 배열이 포함됩니다 - **향상된 메타데이터**: 성별(남성/여성), 연령대(어린이/청소년/청년/중년/노년), 사용 사례 포함 - **고급 필터링**: 모델, 성별, 연령, 사용 사례로 필터링하여 특정 요구사항에 맞는 보이스 찾기 **사용 사례:** - 다양한 필터가 있는 보이스 선택 UI - 특정 콘텐츠 유형에 적합한 보이스 찾기(예: 광고, 오디오북, 교육) - 각 모델 버전에서 사용 가능한 감정 탐색 ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "프로덕션 서버" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v2/voices": { "get": { "tags": [ "Deprecated" ], "x-mint": { "href": "/ko/api-reference/deprecated/list-voices-v2" }, "summary": "보이스 목록 조회 (v2)", "responses": { "200": { "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/VoiceV2" }, "title": "Response Get Voices" }, "example": [ { "age": "young_adult", "gender": "female", "models": [ { "version": "ssfm-v30", "emotions": [ "normal", "happy", "sad", "angry", "whisper", "toneup", "tonedown" ] }, { "version": "ssfm-v21", "emotions": [ "normal", "happy", "sad", "angry" ] } ], "voice_id": "tc_60e5426de8b95f1d3000d7b5", "use_cases": [ "Audiobook", "E-learning", "Ads" ], "voice_name": "Olivia", "voice_type": "original" } ] } }, "description": "Success - Returns list of voice models with enhanced metadata" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Unauthorized - Invalid or missing API key" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid request format" } } }, "description": "Validation Error - Invalid request parameters" } }, "deprecated": true, "parameters": [ { "in": "query", "name": "model", "schema": { "$ref": "#/components/schemas/TTSModel" }, "required": false, "description": "보이스 모델로 필터링(ssfm-v21 또는 ssfm-v30). 지정된 모델을 지원하는 보이스를 반환합니다. 선택 사항 - 제공하지 않으면 모든 모델의 보이스를 반환합니다." }, { "in": "query", "name": "gender", "schema": { "$ref": "#/components/schemas/GenderEnum" }, "required": false, "description": "성별로 필터링(남성 또는 여성). 지정된 성별과 일치하는 보이스를 반환합니다. 선택 사항 - 제공하지 않으면 모든 성별의 보이스를 반환합니다." }, { "in": "query", "name": "age", "schema": { "$ref": "#/components/schemas/AgeEnum" }, "required": false, "description": "연령대로 필터링(어린이, 청소년, 청년, 중년, 노년). 지정된 연령대와 일치하는 보이스를 반환합니다. 선택 사항 - 제공하지 않으면 모든 연령의 보이스를 반환합니다." }, { "in": "query", "name": "use_cases", "schema": { "$ref": "#/components/schemas/UseCasesEnum" }, "required": false, "description": "사용 사례 카테고리로 필터링. 지정된 사용 사례로 태그된 보이스를 반환합니다(TikTok/Reels/Shorts, Game, Audiobook/Storytelling 등). 선택 사항 - 제공하지 않으면 사용 사례에 관계없이 모든 보이스를 반환합니다." }, { "in": "query", "name": "voice_type", "schema": { "$ref": "#/components/schemas/VoiceType" }, "required": false, "description": "보이스 타입으로 필터링 (`original` 또는 `custom`). 선택 사항 - 제공하지 않으면 모든 타입의 보이스를 반환합니다." } ], "description": "향상된 메타데이터 및 필터링 기능을 갖춘 사용 가능한 모든 보이스를 조회합니다(V2).\n\n이 엔드포인트는 모델별로 그룹화된 감정 지원과 성별, 연령대, 사용 사례를 포함한 추가 메타데이터가 있는 향상된 보이스 목록을 반환합니다. 각 보이스는 해당 감정 세트와 함께 여러 모델을 지원할 수 있습니다.\n\n**주요 기능:**\n- **모델 그룹화**: 각 보이스에는 지원되는 모든 TTS 모델과 사용 가능한 감정을 보여주는 `models` 배열이 포함됩니다\n- **향상된 메타데이터**: 성별(남성/여성), 연령대(어린이/청소년/청년/중년/노년), 사용 사례 포함\n- **고급 필터링**: 모델, 성별, 연령, 사용 사례로 필터링하여 특정 요구사항에 맞는 보이스 찾기\n\n**사용 사례:**\n- 다양한 필터가 있는 보이스 선택 UI\n- 특정 콘텐츠 유형에 적합한 보이스 찾기(예: 광고, 오디오북, 교육)\n- 각 모델 버전에서 사용 가능한 감정 탐색", "operationId": "get_voices_v2_v2_voices_get", "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request GET \\\n --url 'https://api.typecast.ai/v2/voices?model=ssfm-v30&gender=female&age=young_adult' \\\n --header 'X-API-KEY: '\n" }, { "lang": "C#", "label": "C# (HttpClient)", "source": "using System;\nusing System.Net.Http;\nusing System.Threading.Tasks;\n\nvar client = new HttpClient();\nclient.DefaultRequestHeaders.Add(\"X-API-KEY\", \"\");\n\nvar response = await client.GetAsync(\"https://api.typecast.ai/v2/voices?model=ssfm-v30&gender=female&age=young_adult\");\n\nif (response.IsSuccessStatusCode)\n{\n var content = await response.Content.ReadAsStringAsync();\n Console.WriteLine(content);\n}\n" }, { "lang": "Kotlin", "label": "Kotlin (OkHttp)", "source": "import okhttp3.OkHttpClient\nimport okhttp3.Request\n\nval client = OkHttpClient()\n\nval request = Request.Builder()\n .url(\"https://api.typecast.ai/v2/voices?model=ssfm-v30&gender=female&age=young_adult\")\n .addHeader(\"X-API-KEY\", \"\")\n .get()\n .build()\n\nclient.newCall(request).execute().use { response ->\n if (response.isSuccessful) {\n println(response.body?.string())\n }\n}\n" }, { "lang": "C++", "label": "C++ (libcurl)", "source": "#include \n#include \n#include \n\nsize_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {\n ((std::string*)userp)->append((char*)contents, size * nmemb);\n return size * nmemb;\n}\n\nint main() {\n CURL* curl = curl_easy_init();\n if(curl) {\n std::string readBuffer;\n struct curl_slist* headers = NULL;\n\n headers = curl_slist_append(headers, \"X-API-KEY: \");\n\n curl_easy_setopt(curl, CURLOPT_URL, \"https://api.typecast.ai/v2/voices?model=ssfm-v30&gender=female&age=young_adult\");\n curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);\n curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);\n curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);\n\n CURLcode res = curl_easy_perform(curl);\n if(res == CURLE_OK) {\n std::cout << readBuffer << std::endl;\n }\n\n curl_slist_free_all(headers);\n curl_easy_cleanup(curl);\n }\n return 0;\n}\n" }, { "lang": "C", "label": "C (libcurl)", "source": "#include \n#include \n#include \n#include \n\ntypedef struct {\n char* data;\n size_t size;\n} MemoryStruct;\n\nsize_t WriteMemoryCallback(void* contents, size_t size, size_t nmemb, void* userp) {\n size_t realsize = size * nmemb;\n MemoryStruct* mem = (MemoryStruct*)userp;\n\n char* ptr = realloc(mem->data, mem->size + realsize + 1);\n if(!ptr) return 0;\n\n mem->data = ptr;\n memcpy(&(mem->data[mem->size]), contents, realsize);\n mem->size += realsize;\n mem->data[mem->size] = 0;\n\n return realsize;\n}\n\nint main(void) {\n CURL* curl;\n CURLcode res;\n MemoryStruct chunk = {NULL, 0};\n\n curl_global_init(CURL_GLOBAL_ALL);\n curl = curl_easy_init();\n\n if(curl) {\n struct curl_slist* headers = NULL;\n headers = curl_slist_append(headers, \"X-API-KEY: \");\n\n curl_easy_setopt(curl, CURLOPT_URL, \"https://api.typecast.ai/v2/voices?model=ssfm-v30&gender=female&age=young_adult\");\n curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);\n curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);\n curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&chunk);\n\n res = curl_easy_perform(curl);\n\n if(res == CURLE_OK) {\n printf(\"%s\\n\", chunk.data);\n }\n\n curl_slist_free_all(headers);\n curl_easy_cleanup(curl);\n free(chunk.data);\n }\n\n curl_global_cleanup();\n return 0;\n}\n" }, { "lang": "Swift", "label": "Swift (URLSession)", "source": "import Foundation\n\nlet url = URL(string: \"https://api.typecast.ai/v2/voices?model=ssfm-v30&gender=female&age=young_adult\")!\nvar request = URLRequest(url: url)\nrequest.httpMethod = \"GET\"\nrequest.setValue(\"\", forHTTPHeaderField: \"X-API-KEY\")\n\nlet task = URLSession.shared.dataTask(with: request) { data, response, error in\n if let data = data, let jsonString = String(data: data, encoding: .utf8) {\n print(jsonString)\n }\n}\ntask.resume()\n" }, { "lang": "Rust", "label": "Rust (reqwest)", "source": "use reqwest;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box> {\n let client = reqwest::Client::new();\n\n let response = client\n .get(\"https://api.typecast.ai/v2/voices?model=ssfm-v30&gender=female&age=young_adult\")\n .header(\"X-API-KEY\", \"\")\n .send()\n .await?;\n\n if response.status().is_success() {\n let body = response.text().await?;\n println!(\"{}\", body);\n }\n\n Ok(())\n}\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "허용되는 최대 동시 요청 수" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "전체 커스텀 보이스 슬롯 한도 (professional 포함)" }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "사용 제한 정보" }, "Prompt": { "title": "프롬프트 (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "적용할 감정 프리셋.\r\n\r\nssfm-v21 지원 감정: normal, happy, sad, angry\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "example": 1, "description": "감정 표현 강도 제어(0.0~2.0).\r\n\r\n- 0.0: 완전히 중립적\r\n- 1.0: 표준 표현(기본값)\r\n- 2.0: 최대 강도\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "연령대 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **child**: 어린이 보이스(12세 미만)\n- **teenager**: 청소년 보이스(13-19세)\n- **young_adult**: 청년 보이스(20-35세)\n- **middle_age**: 중년 보이스(36-60세)\n- **elder**: 노년 보이스(60세 이상)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "플랜에서 기본으로 제공하는 총 크레딧" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "사용된 크레딧 수" } }, "description": "크레딧 사용 정보" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대 분류(어린이/청소년/청년/중년/노년)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별 분류(남성/여성)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "사용 가능한 감정이 있는 지원되는 TTS 모델 목록(예: [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유한 보이스 식별자. 기본 제공 보이스는 `tc_` prefix (예: `tc_60e5426de8b95f1d3000d7b5`), `POST /v1/voices/clone` 으로 만든 커스텀 보이스는 `uc_` prefix 를 사용합니다. 본인이 소유한 `uc_` 보이스도 `/v2/voices` 응답에 포함됩니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "이 보이스가 적합한 사용 사례 카테고리 목록\n\n## 보이스 이름 참고\n\n`voice_name` 필드는 영문으로만 반환됩니다. 아래 목록에서 한국어 표기를 확인하실 수 있습니다.\n\n
\n보이스 이름 목록 펼치기 (542개)\n\n| 영어 | 한국어 | 영어 | 한국어 | 영어 | 한국어 |\n| --- | --- | --- | --- | --- | --- |\n| Aaron | 에런 | Inhye | 인혜 | Piljae | 필재 |\n| Abigail | 아비게일 | Inseong | 인성 | Pyeonghwa | 평화 |\n| Ae-ran | 애란 | Instructor Han | 한유격 교관 | Rachel | 레이첼 |\n| Ael | 아엘 | Insun | 인선 | Ravi | 라비 |\n| Agatha | 아가사 | Jabbaba | 자바바 | Rayeon | 라연 |\n| Aiden | 에이든 | Jack | 잭 | Rebecca | 레베카 |\n| Alena | 알레나 | Jack-o’-Lantern | 잭오랜턴 | Reporter Bona | 다보나 기자 |\n| Alex | 알렉스 | Jackson | 잭슨 | Reporter Catalina | 카탈리나 기자 |\n| Amber | 엠버 | Jaeho | 재호 | Reporter Jinsub | 송진섭 기자 |\n| Anchor Hwa | 김경화 앵커 | Jaehun | 재훈 | Reporter Kang | 강수정 기자 |\n| Angela | 안젤라 | Jaejun | 재준 | Reporter Lee | 이승주 기자 |\n| Anja | 안냐 | Jaekyung | 재경 | Reporter Parkchan | 박찬 기자 |\n| Annabelle the Ghost | 귀신 애나벨 | Jaesun | 재선 | Rex | 렉스 |\n| Annie | 애니 | JaeYi | 재이 | Riley | 라일리 |\n| April | 에이프릴 | Jaeyoung | 재영 | Risan Ji | 지리산 |\n| Arang | 아랑 | Jain | 자인 | Rita | 리타 |\n| Ari | 아리 | Jake | 제이크 | River | 리버 |\n| Arin | 아린 | Janet | 재닛 | Robert | 로버트 |\n| Athena | 아테나 | Jangho | 장호 | Robo | 로보 |\n| Audrey | 오드리 | Jangwoon | 장운 | Romi | 로미 |\n| Avery | 에이버리 | Jechan | 제찬 | Ron | 론 |\n| Avong | 아봉 | Jeff | 제프 | Roro | 로로 |\n| Azzi | 아찌 | Jenna | 제나 | Rowoon | 로운 |\n| Babilon | 바빌론 | Jennifer | 제니퍼 | Royce | 로이스 |\n| Bell | 벨 | Jeong Choi | 최정 | Rudolph | 루돌프 |\n| Ben | 벤 | Jeongah | 정아 | Ruri | 루리 |\n| Benny | 베니 | JeongJin | 정진 | Rusty | 러스티 |\n| Beri | 베리 | Jeongseob | 정섭 | Ryan | 라이언 |\n| Billie | 빌리 | Jerome | 제롬 | Ryueun | 류은 |\n| Bomi | 이보미 | Jewby | 쥬비 | Sabrina the Witch | 마녀 사브리나 |\n| Bonggyu | 봉규 | Jian | 지안 | Saeron | 새론 |\n| Bongman Kim | 김봉만 | Jicheol | 지철 | Salty Chan-gu | 삐뚤어진 찬구 |\n| Bono | 보노 | Jihee | 지희 | Samantha | 사만다 |\n| Bora | 보라 | Jihoon | 지훈 | Sammy | 새미 |\n| Brad | 브래드 | Jihyun | 지현 | Sangdo | 상도 |\n| Bruce | 브루스 | Jimmy | 지미 | Sanghoon | 상훈 |\n| Buddy | 버디 | Jin | 진 | Sangwoo | 상우 |\n| Bumsoo | 범수 | Jinhan | 진한 | Santa | 산타 |\n| Buttaguy | 빠다가이 | Jinhang Kim | 김진행 | Santa Claus | 산타클로스 |\n| Caitlyn | 케이틀린 | Jinhee | 진희 | Santa Reporter | VJ 싼타 |\n| Callan | 칼란 | Jinhyuk | 진혁 | Sara | 세라 |\n| Camila | 카밀라 | Jinseo | 진서 | Sean | 션 |\n| Captain Bill | 캡틴 빌 | Jinung | 진웅 | SeHee | 세희 |\n| Carl | 칼 | Jinwoo | 진우 | Seheon | 세헌 |\n| Carlos | 카를로스 | Jiwoo | 지우 | Sejin | 세진 |\n| Carol | 캐롤 | Jiyoon | 지윤 | Sena | 세나 |\n| Catherine | 캐서린 | Jiyoung | 지영 | Seohee | 서희 |\n| Chad | 채드 | Jodie | 조디 | Seojin | 서진 |\n| Chaeah | 채아 | Jolly | 졸리 | Seojoon | 서준 |\n| Chan-gu | 찬구 | Jongdae | 종대 | Seok Choi | 최석 |\n| Changbae | 창배 | Jooeun | 주은 | SeokPil | 한석필 |\n| Changhee | 창희 | Joonghyun | 중현 | Seokpyo | 석표 |\n| Changmin | 창민 | Joongsik | 중식 | Seolhwa | 설화 |\n| Changsu | 박창수 | Joonkyu | 준규 | Seonha | 선하 |\n| Chanhyuk | 찬혁 | Joshua | 조슈아 | Seoyeon | 서연 |\n| Charlotte | 샬롯 | Juha | 주하 | Seoyoon | 서윤 |\n| Chase | 체이스 | Juho | 주호 | Seungah | 승아 |\n| Cheolhoon | 철훈 | Jungbong | 정봉 | Seungheon | 승헌 |\n| Cheolyong | 리철용 | Junghee | 정희 | Seungho | 승호 |\n| Cherry | 체리 | Jungjae | 정재 | Seunghwa | 승화 |\n| Chester | 체스터 | Jungmin | 정민 | Seungjae | 승재 |\n| Chiho | 치호 | Jungseok | 정석 | Seungmoon | 승문 |\n| Chloe | 클로이 | Jungsoon | 정순 | Seungwon | 승원 |\n| Choyeon | 강초연 교관 | Jungwon | 정원 | Seungyeon | 승연 |\n| Chungah | 청아 | Junhee | 준희 | Sewoo | 세우 |\n| Chunsik Kang | 강춘식 기자 | Junho | 준호 | Shana | 샤나 |\n| Claire | 클레어 | JunKi | 준기 | Sheriff Kim | 김반장 |\n| Classic Narrator | 미스타 변사 | Junsang | 준상 | Shinhe | 신혜 |\n| Cole | 콜 | Junseong | 준성 | Shinwook | 신욱 |\n| Cox | 콕스 | Justice Roh | 정의로 | Shotgun | 샷건 |\n| Cyrus | 사이러스 | Justin | 저스틴 | Simon | 사이먼 |\n| Dabin | 다빈 | Juwon | 주원 | Sindarin | 신다린 |\n| Daegil | 대길 | Juyoung | 주영 | Sio | 시오 |\n| Dahee | 다희 | K-Santa | 산타 할아버지 | Siwon | 시원 |\n| Dahyeon | 다현 | Kangil | 강일 | Siwoo | 시우 |\n| Damian | 데이미언 | Kanno | 칸노 | Siyeon | 시연 |\n| Dan | 댄 | Katie | 케이티 | Skylar | 스카일러 |\n| Dana | 다나 | Kelly | 켈리 | Slushy | 슬러시 |\n| Dasom | 다솜 | Kelsey | 켈시 | Smoke | 스모크 |\n| David | 데이비드 | Kevin | 케빈 | Sohye | 소혜 |\n| Dean | 딘 | Keybo | 키보 | Sojin | 소진 |\n| Deokhwan | 덕환 | Kijang Kim | 김기장 | Soobin | 수빈 |\n| Dohan | 도한 | Killian the Vampire | 뱀파이어 킬리언 | Sookhee | 숙희 |\n| Dohee | 도희 | Kiseob | 기섭 | Sooni | 순이 |\n| Dohyun | 도현 | Klip Kim | 클립 킴 | Sophia | 소피아 |\n| Dollar Jr. | 달러 주니어 | Koombo | 쿰보 | Sora | 소라 |\n| Doug | 더그 | Kristen | 크리스틴 | Soye | 소예 |\n| Doughnut | 도넛 | Kukhee | 국희 | Soyi | 소이 |\n| DU5T | 더스트 | Kwonil | 권일 | Soyoung | 소영 |\n| Duckchun | 덕춘 | Kyumin | 규민 | Soyul | 소율 |\n| Duke | 듀크 | Kyungho | 경호 | Spice | 스파이스 |\n| Dukgu | 덕구 | Kyungsoo | 경수 | Sportscaster Kang | 강수정 캐스터 |\n| Duman | 두만 | Kyungsook | 경숙 | Sportscaster Tony | 이영광 캐스터 |\n| Dupil | 곽두필 | Lady Cho | 단희빈 | Starling | 스탈링 |\n| DVZY | 데이지 | Lala | 라라 | Stephanie | 스테파니 |\n| Dylan | 딜런 | Lamie | 라미 | Storyteller Jinsub | 송진섭 스토리텔러 |\n| E-seller Sherri | 쇼린이 | Landon | 랜던 | Sua | 수아 |\n| Echo | 에코 | Larry | 래리 | Suho | 수호 |\n| Edward | 에드워드 | Leo | 레오 | Suji | 수지 |\n| Elias | 엘리아스 | Liam | 리암 | Sujin | 수진 |\n| Elise | 엘리스 | Lindsay | 린지 | Sullock Hong | 홍설록 |\n| Ella | 엘라 | Liz | 리즈 | Sumin | 수민 |\n| Eman | 이만 | Lloyd | 로이드 | Sungbae | 성배 |\n| Emma | 엠마 | Logan | 로건 | Sunggyu | 성규 |\n| Eunbin | 은빈 | Lucille | 루실 | Sungho | 성호 |\n| Eunchae | 은채 | Luna | 루나 | Sunghoon | 성훈 |\n| Eunha | 은하 | Lydia | 리디아 | Sunghyun | 성현 |\n| Eunsol | 은솔 | Maddie | 매디 | Sungjun | 홈쇼핑 성준 |\n| Frankenstein | 프랑켄슈타인 | Maisie | 메이시 | Sungkwon | 성권 |\n| Furnando | 터르난도 | Margaret | 마가렛 | Sungtae | 성태 |\n| Gaeul | 가을 | Margot | 마고 | Sungwook | 성욱 |\n| Gahee | 가희 | Matthew | 매튜 | Sunyoung | 선영 |\n| George | 조지 | MBTI EF (F) | MBTI EF 여 | Suyoon | 수윤 |\n| GeumHee | 금희 | MBTI EF (M) | MBTI EF 남 | Sylvia | 실비아 |\n| Geunhyeok | 근혁 | MBTI ET (F) | MBTI ET 여 | Taebaek | 장태백 |\n| Geunseok | 근석 | MBTI ET (M) | MBTI ET 남 | Taeji | 태지 |\n| Geunwoo | 근우 | MBTI IF (F) | MBTI IF 여 | Taejoong | 태중 |\n| Geunyeong | 근영 | MBTI IF (M) | MBTI IF 남 | Taemin | 태민 |\n| Ggami | 까미 | MBTI IT (F) | MBTI IT 여 | Taesub | 태섭 |\n| Ggomi | 꼬미 | MBTI IT (M) | MBTI IT 남 | Taewoo | 태우 |\n| Glenda | 글렌다 | MC Kong | MC콩 | Teal | 틸 |\n| Goat Kim | 김고트 | MC TypeCast | MC 타캐 | Tessa | 테사 |\n| Gongchul | 공철 | Mia | 미아 | Tian | 티안 |\n| Gowoon | 고운 | Michael | 마이클 | Tim | 팀 |\n| Grace | 그레이스 | Mija | 오미자 | Tina | 티나 |\n| Graham | 그레이엄 | Mijin | 미진 | Toby | 토비 |\n| Gunseok | 건석 | Mikyung | 미경 | Tommy | 토미 |\n| Gunwoo | 건우 | Millie | 밀리 | Tyson | 타이슨 |\n| Guri | 구리 | Minchae | 민채 | UiChan | 의찬이 |\n| Gus | 거스 | Minji | 민지 | Uncle Hank | 행크 |\n| Ha Eun | 하은 | Minjoon | 민준 | Valerie | 밸러리 |\n| Haejun | 해준 | Minju | 민주 | Valkyrie | 발키리 |\n| Haerang | 해랑 | Minjung | 민정 | Vanessa | 바네사 |\n| Hailey | 헤일리 | Minsang | 민상 | Verna | 베르나 |\n| Hajun | 하준 | Minsu | 민수 | Victoria | 빅토리아 |\n| Hakchul | 학철 | Mio | 미오 | Viktor | 빅토르 |\n| Hamchu | 햄쮸 | Miran Choi | 최미란 | Vincent | 빈센트 |\n| Han Taesung Caster | 한태성 캐스터 | Mirine | 미리내 | Vivien | 비비안 |\n| Hana | 하나 | Miseon | 미선 | Wade | 웨이드 |\n| Hangyeol | 한결 | Miso | 미소 | Walter | 월터 |\n| Hanjun | 한준 | Mister Gop | 미스터 갑 | Wangkwon | 왕권 |\n| Hanna | 한나 | Monggun | 몽군 | Weather Reporter Sky | 하늘 캐스터 |\n| Hans | 한스 | Moonjung | 문정 | West | 웨스트 |\n| Hansol | 한솔 | Mooyeol | 무열 | Wonho | 원호 |\n| Hanyoung | 한영 | Morgan | 모건 | Wonkyung | 원경 |\n| Harin | 하린 | Moru | 머루 | Wonwoo | 원우 |\n| Harper | 하퍼 | Mrs. Claus | 미세스 클로스 | Wooju | 우주 |\n| Hayul | 하율 | Munseok | 문석 | Woosung | 우성 |\n| Heejoon | 희준 | Munsu | 문수 | Xavier | 자비에르 |\n| Helena | 헬레나 | Myeonghee | 명희 | Yeeun | 예은 |\n| Henry | 헨리 | Myungil | 명일 | Yejin | 예진 |\n| Homun Sim | 심호문 | Myungjoo | 명주 | Yejoon | 예준 |\n| Hoon | 훈 | Naeun | 나은 | YeLin | 예린 |\n| Hosik | 호식 | Najin | 나진 | Yena | 예나 |\n| Hosun | 호선 | Namjoon | 남준 | Yeonah | 연아 |\n| Hoyoung | 호영 | Nana | 나나 | Yeonggeol | 영걸 |\n| Hugh | 휴 | Nari | 개나리 | Yeonhwa | 연화 |\n| Hwimin | 휘민 | Nathan | 네이선 | Yeonja | 연자 |\n| Hyejung | 혜정 | Neel | 닐 | Yeonsuh | 연서 |\n| Hyelee | 혜리 | Neoguard | 네오가드 | Yeonwoo | 연우 |\n| Hyemin | 혜민 | News anchor Jinsub | 송진섭 앵커 | Yeseul | 예슬 |\n| Hyena | 혜나 | Newscaster John | 존 앵커 | Yongsik | 용식이 |\n| Hyeongjin | 형진 | Nia | 니아 | Yoonseo | 윤서 |\n| Hyera | 혜라 | Noa | 노아 | Yoonsung | 윤성 |\n| Hyesu | 홈쇼핑 혜수 | Noel | 노엘 | Younggil | 영길 |\n| Hyewon | 혜원 | Noeul | 노을 | Younghee | 영희 |\n| Hyun | 현 | Norah | 노라 | Younghwan | 영환 |\n| Hyunji | 현지 | Nova | 노바 | Youngji | 영지 |\n| Hyunjin | 현진 | Old radio | 레디오 | Youngkyu | 영규 |\n| Hyunju | 현주 | Oliver | 올리버 | Youngmok | 영목 |\n| Hyunkyung | 현경 | Olivia | 올리비아 | Yubin | 유빈 |\n| Hyunmin | 현민 | Oscar | 오스카 | Yujin | 유진 |\n| Hyunseung | 현승 | Owen | 오언 | Yumi | 유미 |\n| HyunWoo | 현우 | P-0150N | 포이즌 | Yumin | 유민 |\n| Ian | 이안 | Pacang | 파캉 | Yunbin | 윤빈 |\n| Icarus | 이카루스 | Paige | 페이지 | Yunjeong | 윤정 |\n| Igyeom | 이겸 | Pangpang | 팡팡 | Yura | 유라 |\n| Ijun | 이준 | Patricia | 패트리샤 | Yuri | 유리 |\n| Ilho | 일호 | Patrick | 패트릭 | Yuseong | 유성 |\n| Ina | 이나 | Peter | 피터 | Zoey | 조이 |\n| Inhwa | 인화 | Philip | 필립 | | |\n\n
\n" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "사람이 읽을 수 있는 보이스 이름" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입 — `original` 은 타입캐스트가 기본 제공하는 스톡 보이스, `custom` 은 사용자가 업로드/클론한 보이스입니다." } }, "description": "모델별로 그룹화된 감정과 향상된 메타데이터가 있는 V2 보이스 응답 모델" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대입니다." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별(`male` 또는 `female`)입니다." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "지원하는 TTS 모델과 감정 목록입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유 보이스 식별자입니다. 기본 보이스는 `tc_`, 커스텀 보이스는 `uc_`로 시작합니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "추천 사용 사례 목록입니다." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "`{\"eng\": \"Daejin\", \"kor\": \"대진\"}` 같은 다국어 보이스 이름입니다." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입(`original` 또는 `custom`)입니다." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "제공되는 경우 사용할 수 있는 미리듣기 오디오 URL입니다." } }, "description": "다국어 이름과 미리듣기 URL을 포함한 보이스 정보입니다." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "API 플랜.\n\n사용 가능한 값:\n- **free**: 제한된 크레딧의 무료 등급\n- **lite**: 중간 수준 크레딧의 라이트 플랜\n- **plus**: 높은 크레딧의 플러스 플랜\n- **custom**: 커스텀 엔터프라이즈 플랜" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "음성 합성에 사용할 TTS 모델 버전. 다양한 모델은 다양한 기능과 품질 수준을 제공합니다.\n\n사용 가능한 모델:\n- **ssfm-v30**: 향상된 플로우와 추가 감정 프리셋이 있는 최신 모델(권장)\n- **ssfm-v21**: 검증된 신뢰성과 일관된 품질을 갖춘 안정적인 모델\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS 모델 버전(예: ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "이 모델에서 지원되는 감정 목록" } }, "description": "버전 및 지원되는 감정을 포함한 모델 정보" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "영어 보이스 이름입니다." }, "kor": { "type": "string", "title": "Kor", "description": "한국어 보이스 이름입니다." } }, "description": "ISO 639-3 언어 코드를 키로 사용하는 다국어 보이스 이름입니다." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "보이스 타입 분류.\n\n- `original` — 타입캐스트가 기본 제공하는 스톡 보이스로, 모든 계정에서 사용 가능합니다.\n- `custom` — 사용자가 본인의 샘플을 업로드하거나 클로닝해서 만든 보이스입니다." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "성별 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **male**: 남성 보이스\n- **female**: 여성 보이스\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "음성 합성에 사용 가능한 감정 프리셋. 각 감정은 생성된 음성의 톤, 속도, 표현력에 영향을 줍니다.\n\n**ssfm-v21 지원 감정 (4종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n\n**ssfm-v30 지원 감정 (7종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n- whisper: 부드럽고 조용한 말\n- toneup: 더 높은 톤 강조\n- tonedown: 더 낮은 톤 강조\n\n/v2/voices API 응답을 통해 각 음성에 사용 가능한 감정을 확인하세요.\n" }, "SmartPrompt": { "type": "object", "title": "스마트 프롬프트 (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "TTSRequest의 `text` 필드 이후에 오는 텍스트. 감정 추론을 위한 전방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 전환을 예측하는 데 도움\r\n- 다음 컨텍스트가 없으면 비워 둡니다\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "프롬프트 유형을 식별하는 판별자 필드. 컨텍스트 인식 감정 추론을 위해 \"smart\"로 설정해야 합니다.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "TTSRequest의 `text` 필드 이전에 오는 텍스트. 감정 추론을 위한 후방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 빌드업과 컨텍스트를 이해하는 데 도움\r\n- 이전 컨텍스트가 없으면 비워 둡니다\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "프리셋 프롬프트 (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "프롬프트 유형을 식별하는 판별자 필드. 프리셋 기반 감정 제어를 위해 \"preset\"으로 설정해야 합니다.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "생성된 음성에 적용할 감정 프리셋.\r\n\r\n지원되는 감정: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "생성된 음성의 감정 표현 강도를 제어합니다.\r\n\r\n- 0.0: 완전히 중립적, 감정 색채 없음\r\n- 0.5: 미묘한 감정 힌트\r\n- 1.0: 표준 감정 표현(기본값)\r\n- 1.5: 강한 감정 강조\r\n- 2.0: 최대 강도, 매우 표현력 있음\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "콘텐츠 유형 필터링을 위한 보이스 사용 사례 카테고리. 각 보이스는 특정 콘텐츠 유형에 대한 적합성을 나타내는 하나 이상의 사용 사례로 태그됩니다.\n\n**사용 가능한 사용 사례:**\n- **Announcer**: 공공 발표 및 프레젠테이션\n- **Anime**: 애니메이션 보이스\n- **Audiobook**: 장문 내레이션 및 스토리텔링\n- **Conversational**: 챗봇 및 대화형 AI\n- **Documentary**: 다큐멘터리 내레이션 및 해설\n- **E-learning**: 교육 콘텐츠 및 튜토리얼\n- **Rapper**: 랩 및 음악 퍼포먼스\n- **Game**: 비디오 게임 보이스 및 내레이션\n- **Tiktok/Reels**: SNS 숏츠\n- **News**: 뉴스 방송\n- **Podcast**: 방송 및 팟캐스트 제작\n- **Voicemail**: IVR 시스템 및 음성 비서\n- **Ads**: 광고 및 홍보 콘텐츠\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "문제를 설명하는 오류 메시지" }, "message": { "type": "string", "description": "구조화된 오류에 대한 설명" }, "error_code": { "type": "string", "description": "구조화된 오류를 식별하는 코드" } }, "description": "기존 오류와 유효성 검사 오류는 `detail`을 사용하며, 구조화된 오류는 `error_code`와 `message`를 사용합니다." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "`status`가 `failed`일 때의 실패 사유입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "source": { "type": "string", "title": "Source", "description": "생성 방식(`instant` 또는 `professional`)입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "UTC 기준 생성 시각입니다." } }, "description": "인증된 계정이 소유한 커스텀 보이스입니다." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "추천 관련도 점수. 값이 높을수록 검색어와 더 잘 맞는 후보입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "`tc_` prefix 가 붙은 타입캐스트 보이스 식별자. 텍스트 음성 변환 요청의 `voice_id` 로 사용할 수 있습니다." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "사람이 읽을 수 있는 보이스 이름." } }, "description": "`GET /v1/voices/recommendations` 응답의 보이스 추천 후보. 관련도 순서로 정렬됩니다." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "커스텀 보이스의 현재 생성 또는 학습 상태입니다." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름 (1~30자)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "보이스가 클로닝된 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "`uc_` prefix 가 붙은 커스텀 보이스 식별자. `POST /v1/text-to-speech` 등 `voice_id` 를 받는 엔드포인트에 그대로 사용할 수 있습니다." } }, "description": "`POST /v1/voices/clone` 응답 — 퀵 클로닝으로 생성된 커스텀 보이스 메타데이터." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "세그먼트 구분값입니다. 항상 `pause`입니다." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "초 단위 쉼 길이입니다. 개별 쉼은 최대 10초, 모든 쉼의 합은 최대 60초입니다.", "exclusiveMinimum": 0 } }, "description": "크레딧 차감 없이 삽입되는 무음 구간입니다.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 단어 단위 정렬 구간." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "현재 구독 플랜명" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "사용 제한 정보" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "크레딧 사용 정보" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "구독 정보 응답 모델" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호/공백 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 문자 단위 정렬 구간." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." } }, "description": "퀵 클로닝 또는 프리미엄 클로닝 생성 요청 결과입니다." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "base64 로 인코딩된 오디오 바이트. `audio_format` 확장자로 디코딩해 파일로 저장할 수 있습니다." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "단어 단위 타임스탬프(문장부호 포함). 요청이 `granularity=char` 일 때는 `null`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "문자 단위 타임스탬프(문장부호와 공백 포함). 요청이 `granularity=word` 일 때는 `null`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "`audio` 필드의 오디오 인코딩 포맷 — `wav` 또는 `mp3` (요청의 `output.audio_format` 에 따라 결정)." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "생성된 오디오의 길이(초)." } }, "description": "TTS 생성 + 타임스탬프 정렬 통합 응답." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "오디오 샘플. WAV 또는 MP3, 최대 25MB, 5초 이상 150초 이하.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "보이스 이름 (1~30자)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "클로닝할 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." } }, "description": "퀵 클로닝 요청을 위한 multipart 본문." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "파일", "format": "binary", "description": "25 MiB 이하이면서 길이가 5초 이상 150초 이하인 WAV 또는 MP3 음성 파일 1개입니다.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "상품 내레이터", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." } }, "description": "퀵 클로닝용 multipart 요청 본문입니다." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "커스텀 보이스 이름", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "파일", "description": "학습에 사용할 WAV 또는 MP3 음성 파일입니다. 하나의 파일을 업로드할 수 있습니다.\r\n\r\n**업로드 제한**\r\n\r\n* WAV 또는 MP3 파일 1개\r\n* 파일 크기: 1 GiB 이하\r\n* 길이: 5분 이상 3시간 이하\r\n* 샘플레이트: 16 kHz 이상\r\n\r\n최상의 결과를 위해 다음 조건을 충족하는 오디오를 권장합니다.\r\n\r\n* 생성된 보이스가 재현하기를 원하는 말투와 최대한 가깝게 녹음해 주세요.\r\n* 배경 소음이 없는 조용한 환경에서 녹음해 주세요.\r\n* 한 명의 화자 목소리만 포함해 주세요.\r\n* `language` 필드에 지정한 언어로 녹음해 주세요.\r\n* 오디오 길이가 길수록 생성되는 보이스의 품질이 향상됩니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." }, "language": { "type": "string", "title": "Language", "example": "kor", "description": "`kor`, `eng` 같은 ISO 639-3 언어 코드입니다." } }, "description": "프리미엄 클로닝용 multipart 요청 본문입니다." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "인증을 위한 API 키. 타입캐스트 API 콘솔에서 API 키를 생성할 수 있습니다." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 보이스 상세 정보 조회 (v2) > 향상된 메타데이터가 있는 특정 보이스에 대한 자세한 정보를 검색합니다(V2). 이 엔드포인트는 모델별로 그룹화된 감정 지원과 성별, 연령대, 사용 사례와 같은 메타데이터를 포함한 단일 보이스에 대한 전체 정보를 반환합니다. TTS 요청을 하기 전에 보이스 세부 정보를 확인하거나 사용 가능한 감정을 확인해야 할 때 사용합니다. **응답 포함 사항:** - **voice_id**: 고유한 보이스 식별자 - **voice_name**: 사람이 읽을 수 있는 보이스 이름 - **models**: 각각의 감정 세트가 있는 지원되는 TTS 모델 배열 - **gender**: 보이스 성별 분류(남성/여성) - **age**: 연령대 분류(어린이/청소년/청년/중년/노년) - **use_cases**: 이 보이스에 권장되는 콘텐츠 카테고리 **사용 사례:** - TTS 요청 전 보이스 가용성 확인 - 특정 보이스 및 모델 조합에 대해 지원되는 감정 확인 - 보이스 선택 UI에 보이스 세부 정보 표시 ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "프로덕션 서버" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v2/voices/{voice_id}": { "get": { "tags": [ "Deprecated" ], "x-mint": { "href": "/ko/api-reference/deprecated/get-voice-details-v2" }, "summary": "보이스 상세 정보 조회 (v2)", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/VoiceV2" }, "example": { "age": "young_adult", "gender": "female", "models": [ { "version": "ssfm-v30", "emotions": [ "normal", "happy", "sad", "angry", "whisper", "toneup", "tonedown" ] }, { "version": "ssfm-v21", "emotions": [ "normal", "happy", "sad", "angry" ] } ], "voice_id": "tc_60e5426de8b95f1d3000d7b5", "use_cases": [ "Audiobook", "E-learning", "Ads" ], "voice_name": "Olivia", "voice_type": "original" } } }, "description": "Success - Returns detailed information for the requested voice" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Unauthorized - Invalid or missing API key" }, "404": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Voice not found" } } }, "description": "Not Found - Requested voice does not exist" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid request format" } } }, "description": "Validation Error - Invalid voice_id format" } }, "deprecated": true, "parameters": [ { "in": "path", "name": "voice_id", "schema": { "type": "string", "title": "Voice Id" }, "required": true } ], "description": "향상된 메타데이터가 있는 특정 보이스에 대한 자세한 정보를 검색합니다(V2).\n\n이 엔드포인트는 모델별로 그룹화된 감정 지원과 성별, 연령대, 사용 사례와 같은 메타데이터를 포함한 단일 보이스에 대한 전체 정보를 반환합니다. TTS 요청을 하기 전에 보이스 세부 정보를 확인하거나 사용 가능한 감정을 확인해야 할 때 사용합니다.\n\n**응답 포함 사항:**\n- **voice_id**: 고유한 보이스 식별자\n- **voice_name**: 사람이 읽을 수 있는 보이스 이름\n- **models**: 각각의 감정 세트가 있는 지원되는 TTS 모델 배열\n- **gender**: 보이스 성별 분류(남성/여성)\n- **age**: 연령대 분류(어린이/청소년/청년/중년/노년)\n- **use_cases**: 이 보이스에 권장되는 콘텐츠 카테고리\n\n**사용 사례:**\n- TTS 요청 전 보이스 가용성 확인\n- 특정 보이스 및 모델 조합에 대해 지원되는 감정 확인\n- 보이스 선택 UI에 보이스 세부 정보 표시", "operationId": "get_voice_v2_v2_voices__voice_id__get", "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request GET \\\n --url 'https://api.typecast.ai/v2/voices/tc_60e5426de8b95f1d3000d7b5' \\\n --header 'X-API-KEY: '\n" }, { "lang": "C#", "label": "C# (HttpClient)", "source": "using System;\nusing System.Net.Http;\nusing System.Threading.Tasks;\n\nvar client = new HttpClient();\nclient.DefaultRequestHeaders.Add(\"X-API-KEY\", \"\");\n\nvar voiceId = \"tc_60e5426de8b95f1d3000d7b5\";\nvar response = await client.GetAsync($\"https://api.typecast.ai/v2/voices/{voiceId}\");\n\nif (response.IsSuccessStatusCode)\n{\n var content = await response.Content.ReadAsStringAsync();\n Console.WriteLine(content);\n}\n" }, { "lang": "Kotlin", "label": "Kotlin (OkHttp)", "source": "import okhttp3.OkHttpClient\nimport okhttp3.Request\n\nval client = OkHttpClient()\nval voiceId = \"tc_60e5426de8b95f1d3000d7b5\"\n\nval request = Request.Builder()\n .url(\"https://api.typecast.ai/v2/voices/$voiceId\")\n .addHeader(\"X-API-KEY\", \"\")\n .get()\n .build()\n\nclient.newCall(request).execute().use { response ->\n if (response.isSuccessful) {\n println(response.body?.string())\n }\n}\n" }, { "lang": "C++", "label": "C++ (libcurl)", "source": "#include \n#include \n#include \n\nsize_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {\n ((std::string*)userp)->append((char*)contents, size * nmemb);\n return size * nmemb;\n}\n\nint main() {\n CURL* curl = curl_easy_init();\n if(curl) {\n std::string readBuffer;\n struct curl_slist* headers = NULL;\n\n headers = curl_slist_append(headers, \"X-API-KEY: \");\n\n std::string url = \"https://api.typecast.ai/v2/voices/tc_60e5426de8b95f1d3000d7b5\";\n\n curl_easy_setopt(curl, CURLOPT_URL, url.c_str());\n curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);\n curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);\n curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);\n\n CURLcode res = curl_easy_perform(curl);\n if(res == CURLE_OK) {\n std::cout << readBuffer << std::endl;\n }\n\n curl_slist_free_all(headers);\n curl_easy_cleanup(curl);\n }\n return 0;\n}\n" }, { "lang": "C", "label": "C (libcurl)", "source": "#include \n#include \n#include \n#include \n\ntypedef struct {\n char* data;\n size_t size;\n} MemoryStruct;\n\nsize_t WriteMemoryCallback(void* contents, size_t size, size_t nmemb, void* userp) {\n size_t realsize = size * nmemb;\n MemoryStruct* mem = (MemoryStruct*)userp;\n\n char* ptr = realloc(mem->data, mem->size + realsize + 1);\n if(!ptr) return 0;\n\n mem->data = ptr;\n memcpy(&(mem->data[mem->size]), contents, realsize);\n mem->size += realsize;\n mem->data[mem->size] = 0;\n\n return realsize;\n}\n\nint main(void) {\n CURL* curl;\n CURLcode res;\n MemoryStruct chunk = {NULL, 0};\n\n curl_global_init(CURL_GLOBAL_ALL);\n curl = curl_easy_init();\n\n if(curl) {\n struct curl_slist* headers = NULL;\n headers = curl_slist_append(headers, \"X-API-KEY: \");\n\n const char* url = \"https://api.typecast.ai/v2/voices/tc_60e5426de8b95f1d3000d7b5\";\n\n curl_easy_setopt(curl, CURLOPT_URL, url);\n curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);\n curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);\n curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&chunk);\n\n res = curl_easy_perform(curl);\n\n if(res == CURLE_OK) {\n printf(\"%s\\n\", chunk.data);\n }\n\n curl_slist_free_all(headers);\n curl_easy_cleanup(curl);\n free(chunk.data);\n }\n\n curl_global_cleanup();\n return 0;\n}\n" }, { "lang": "Swift", "label": "Swift (URLSession)", "source": "import Foundation\n\nlet voiceId = \"tc_60e5426de8b95f1d3000d7b5\"\nlet url = URL(string: \"https://api.typecast.ai/v2/voices/\\(voiceId)\")!\nvar request = URLRequest(url: url)\nrequest.httpMethod = \"GET\"\nrequest.setValue(\"\", forHTTPHeaderField: \"X-API-KEY\")\n\nlet task = URLSession.shared.dataTask(with: request) { data, response, error in\n if let data = data, let jsonString = String(data: data, encoding: .utf8) {\n print(jsonString)\n }\n}\ntask.resume()\n" }, { "lang": "Rust", "label": "Rust (reqwest)", "source": "use reqwest;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box> {\n let client = reqwest::Client::new();\n let voice_id = \"tc_60e5426de8b95f1d3000d7b5\";\n\n let url = format!(\"https://api.typecast.ai/v2/voices/{}\", voice_id);\n\n let response = client\n .get(&url)\n .header(\"X-API-KEY\", \"\")\n .send()\n .await?;\n\n if response.status().is_success() {\n let body = response.text().await?;\n println!(\"{}\", body);\n }\n\n Ok(())\n}\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "허용되는 최대 동시 요청 수" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "전체 커스텀 보이스 슬롯 한도 (professional 포함)" }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "사용 제한 정보" }, "Prompt": { "title": "프롬프트 (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "적용할 감정 프리셋.\r\n\r\nssfm-v21 지원 감정: normal, happy, sad, angry\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "example": 1, "description": "감정 표현 강도 제어(0.0~2.0).\r\n\r\n- 0.0: 완전히 중립적\r\n- 1.0: 표준 표현(기본값)\r\n- 2.0: 최대 강도\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "연령대 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **child**: 어린이 보이스(12세 미만)\n- **teenager**: 청소년 보이스(13-19세)\n- **young_adult**: 청년 보이스(20-35세)\n- **middle_age**: 중년 보이스(36-60세)\n- **elder**: 노년 보이스(60세 이상)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "플랜에서 기본으로 제공하는 총 크레딧" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "사용된 크레딧 수" } }, "description": "크레딧 사용 정보" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대 분류(어린이/청소년/청년/중년/노년)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별 분류(남성/여성)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "사용 가능한 감정이 있는 지원되는 TTS 모델 목록(예: [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유한 보이스 식별자. 기본 제공 보이스는 `tc_` prefix (예: `tc_60e5426de8b95f1d3000d7b5`), `POST /v1/voices/clone` 으로 만든 커스텀 보이스는 `uc_` prefix 를 사용합니다. 본인이 소유한 `uc_` 보이스도 `/v2/voices` 응답에 포함됩니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "이 보이스가 적합한 사용 사례 카테고리 목록\n\n## 보이스 이름 참고\n\n`voice_name` 필드는 영문으로만 반환됩니다. 아래 목록에서 한국어 표기를 확인하실 수 있습니다.\n\n
\n보이스 이름 목록 펼치기 (542개)\n\n| 영어 | 한국어 | 영어 | 한국어 | 영어 | 한국어 |\n| --- | --- | --- | --- | --- | --- |\n| Aaron | 에런 | Inhye | 인혜 | Piljae | 필재 |\n| Abigail | 아비게일 | Inseong | 인성 | Pyeonghwa | 평화 |\n| Ae-ran | 애란 | Instructor Han | 한유격 교관 | Rachel | 레이첼 |\n| Ael | 아엘 | Insun | 인선 | Ravi | 라비 |\n| Agatha | 아가사 | Jabbaba | 자바바 | Rayeon | 라연 |\n| Aiden | 에이든 | Jack | 잭 | Rebecca | 레베카 |\n| Alena | 알레나 | Jack-o’-Lantern | 잭오랜턴 | Reporter Bona | 다보나 기자 |\n| Alex | 알렉스 | Jackson | 잭슨 | Reporter Catalina | 카탈리나 기자 |\n| Amber | 엠버 | Jaeho | 재호 | Reporter Jinsub | 송진섭 기자 |\n| Anchor Hwa | 김경화 앵커 | Jaehun | 재훈 | Reporter Kang | 강수정 기자 |\n| Angela | 안젤라 | Jaejun | 재준 | Reporter Lee | 이승주 기자 |\n| Anja | 안냐 | Jaekyung | 재경 | Reporter Parkchan | 박찬 기자 |\n| Annabelle the Ghost | 귀신 애나벨 | Jaesun | 재선 | Rex | 렉스 |\n| Annie | 애니 | JaeYi | 재이 | Riley | 라일리 |\n| April | 에이프릴 | Jaeyoung | 재영 | Risan Ji | 지리산 |\n| Arang | 아랑 | Jain | 자인 | Rita | 리타 |\n| Ari | 아리 | Jake | 제이크 | River | 리버 |\n| Arin | 아린 | Janet | 재닛 | Robert | 로버트 |\n| Athena | 아테나 | Jangho | 장호 | Robo | 로보 |\n| Audrey | 오드리 | Jangwoon | 장운 | Romi | 로미 |\n| Avery | 에이버리 | Jechan | 제찬 | Ron | 론 |\n| Avong | 아봉 | Jeff | 제프 | Roro | 로로 |\n| Azzi | 아찌 | Jenna | 제나 | Rowoon | 로운 |\n| Babilon | 바빌론 | Jennifer | 제니퍼 | Royce | 로이스 |\n| Bell | 벨 | Jeong Choi | 최정 | Rudolph | 루돌프 |\n| Ben | 벤 | Jeongah | 정아 | Ruri | 루리 |\n| Benny | 베니 | JeongJin | 정진 | Rusty | 러스티 |\n| Beri | 베리 | Jeongseob | 정섭 | Ryan | 라이언 |\n| Billie | 빌리 | Jerome | 제롬 | Ryueun | 류은 |\n| Bomi | 이보미 | Jewby | 쥬비 | Sabrina the Witch | 마녀 사브리나 |\n| Bonggyu | 봉규 | Jian | 지안 | Saeron | 새론 |\n| Bongman Kim | 김봉만 | Jicheol | 지철 | Salty Chan-gu | 삐뚤어진 찬구 |\n| Bono | 보노 | Jihee | 지희 | Samantha | 사만다 |\n| Bora | 보라 | Jihoon | 지훈 | Sammy | 새미 |\n| Brad | 브래드 | Jihyun | 지현 | Sangdo | 상도 |\n| Bruce | 브루스 | Jimmy | 지미 | Sanghoon | 상훈 |\n| Buddy | 버디 | Jin | 진 | Sangwoo | 상우 |\n| Bumsoo | 범수 | Jinhan | 진한 | Santa | 산타 |\n| Buttaguy | 빠다가이 | Jinhang Kim | 김진행 | Santa Claus | 산타클로스 |\n| Caitlyn | 케이틀린 | Jinhee | 진희 | Santa Reporter | VJ 싼타 |\n| Callan | 칼란 | Jinhyuk | 진혁 | Sara | 세라 |\n| Camila | 카밀라 | Jinseo | 진서 | Sean | 션 |\n| Captain Bill | 캡틴 빌 | Jinung | 진웅 | SeHee | 세희 |\n| Carl | 칼 | Jinwoo | 진우 | Seheon | 세헌 |\n| Carlos | 카를로스 | Jiwoo | 지우 | Sejin | 세진 |\n| Carol | 캐롤 | Jiyoon | 지윤 | Sena | 세나 |\n| Catherine | 캐서린 | Jiyoung | 지영 | Seohee | 서희 |\n| Chad | 채드 | Jodie | 조디 | Seojin | 서진 |\n| Chaeah | 채아 | Jolly | 졸리 | Seojoon | 서준 |\n| Chan-gu | 찬구 | Jongdae | 종대 | Seok Choi | 최석 |\n| Changbae | 창배 | Jooeun | 주은 | SeokPil | 한석필 |\n| Changhee | 창희 | Joonghyun | 중현 | Seokpyo | 석표 |\n| Changmin | 창민 | Joongsik | 중식 | Seolhwa | 설화 |\n| Changsu | 박창수 | Joonkyu | 준규 | Seonha | 선하 |\n| Chanhyuk | 찬혁 | Joshua | 조슈아 | Seoyeon | 서연 |\n| Charlotte | 샬롯 | Juha | 주하 | Seoyoon | 서윤 |\n| Chase | 체이스 | Juho | 주호 | Seungah | 승아 |\n| Cheolhoon | 철훈 | Jungbong | 정봉 | Seungheon | 승헌 |\n| Cheolyong | 리철용 | Junghee | 정희 | Seungho | 승호 |\n| Cherry | 체리 | Jungjae | 정재 | Seunghwa | 승화 |\n| Chester | 체스터 | Jungmin | 정민 | Seungjae | 승재 |\n| Chiho | 치호 | Jungseok | 정석 | Seungmoon | 승문 |\n| Chloe | 클로이 | Jungsoon | 정순 | Seungwon | 승원 |\n| Choyeon | 강초연 교관 | Jungwon | 정원 | Seungyeon | 승연 |\n| Chungah | 청아 | Junhee | 준희 | Sewoo | 세우 |\n| Chunsik Kang | 강춘식 기자 | Junho | 준호 | Shana | 샤나 |\n| Claire | 클레어 | JunKi | 준기 | Sheriff Kim | 김반장 |\n| Classic Narrator | 미스타 변사 | Junsang | 준상 | Shinhe | 신혜 |\n| Cole | 콜 | Junseong | 준성 | Shinwook | 신욱 |\n| Cox | 콕스 | Justice Roh | 정의로 | Shotgun | 샷건 |\n| Cyrus | 사이러스 | Justin | 저스틴 | Simon | 사이먼 |\n| Dabin | 다빈 | Juwon | 주원 | Sindarin | 신다린 |\n| Daegil | 대길 | Juyoung | 주영 | Sio | 시오 |\n| Dahee | 다희 | K-Santa | 산타 할아버지 | Siwon | 시원 |\n| Dahyeon | 다현 | Kangil | 강일 | Siwoo | 시우 |\n| Damian | 데이미언 | Kanno | 칸노 | Siyeon | 시연 |\n| Dan | 댄 | Katie | 케이티 | Skylar | 스카일러 |\n| Dana | 다나 | Kelly | 켈리 | Slushy | 슬러시 |\n| Dasom | 다솜 | Kelsey | 켈시 | Smoke | 스모크 |\n| David | 데이비드 | Kevin | 케빈 | Sohye | 소혜 |\n| Dean | 딘 | Keybo | 키보 | Sojin | 소진 |\n| Deokhwan | 덕환 | Kijang Kim | 김기장 | Soobin | 수빈 |\n| Dohan | 도한 | Killian the Vampire | 뱀파이어 킬리언 | Sookhee | 숙희 |\n| Dohee | 도희 | Kiseob | 기섭 | Sooni | 순이 |\n| Dohyun | 도현 | Klip Kim | 클립 킴 | Sophia | 소피아 |\n| Dollar Jr. | 달러 주니어 | Koombo | 쿰보 | Sora | 소라 |\n| Doug | 더그 | Kristen | 크리스틴 | Soye | 소예 |\n| Doughnut | 도넛 | Kukhee | 국희 | Soyi | 소이 |\n| DU5T | 더스트 | Kwonil | 권일 | Soyoung | 소영 |\n| Duckchun | 덕춘 | Kyumin | 규민 | Soyul | 소율 |\n| Duke | 듀크 | Kyungho | 경호 | Spice | 스파이스 |\n| Dukgu | 덕구 | Kyungsoo | 경수 | Sportscaster Kang | 강수정 캐스터 |\n| Duman | 두만 | Kyungsook | 경숙 | Sportscaster Tony | 이영광 캐스터 |\n| Dupil | 곽두필 | Lady Cho | 단희빈 | Starling | 스탈링 |\n| DVZY | 데이지 | Lala | 라라 | Stephanie | 스테파니 |\n| Dylan | 딜런 | Lamie | 라미 | Storyteller Jinsub | 송진섭 스토리텔러 |\n| E-seller Sherri | 쇼린이 | Landon | 랜던 | Sua | 수아 |\n| Echo | 에코 | Larry | 래리 | Suho | 수호 |\n| Edward | 에드워드 | Leo | 레오 | Suji | 수지 |\n| Elias | 엘리아스 | Liam | 리암 | Sujin | 수진 |\n| Elise | 엘리스 | Lindsay | 린지 | Sullock Hong | 홍설록 |\n| Ella | 엘라 | Liz | 리즈 | Sumin | 수민 |\n| Eman | 이만 | Lloyd | 로이드 | Sungbae | 성배 |\n| Emma | 엠마 | Logan | 로건 | Sunggyu | 성규 |\n| Eunbin | 은빈 | Lucille | 루실 | Sungho | 성호 |\n| Eunchae | 은채 | Luna | 루나 | Sunghoon | 성훈 |\n| Eunha | 은하 | Lydia | 리디아 | Sunghyun | 성현 |\n| Eunsol | 은솔 | Maddie | 매디 | Sungjun | 홈쇼핑 성준 |\n| Frankenstein | 프랑켄슈타인 | Maisie | 메이시 | Sungkwon | 성권 |\n| Furnando | 터르난도 | Margaret | 마가렛 | Sungtae | 성태 |\n| Gaeul | 가을 | Margot | 마고 | Sungwook | 성욱 |\n| Gahee | 가희 | Matthew | 매튜 | Sunyoung | 선영 |\n| George | 조지 | MBTI EF (F) | MBTI EF 여 | Suyoon | 수윤 |\n| GeumHee | 금희 | MBTI EF (M) | MBTI EF 남 | Sylvia | 실비아 |\n| Geunhyeok | 근혁 | MBTI ET (F) | MBTI ET 여 | Taebaek | 장태백 |\n| Geunseok | 근석 | MBTI ET (M) | MBTI ET 남 | Taeji | 태지 |\n| Geunwoo | 근우 | MBTI IF (F) | MBTI IF 여 | Taejoong | 태중 |\n| Geunyeong | 근영 | MBTI IF (M) | MBTI IF 남 | Taemin | 태민 |\n| Ggami | 까미 | MBTI IT (F) | MBTI IT 여 | Taesub | 태섭 |\n| Ggomi | 꼬미 | MBTI IT (M) | MBTI IT 남 | Taewoo | 태우 |\n| Glenda | 글렌다 | MC Kong | MC콩 | Teal | 틸 |\n| Goat Kim | 김고트 | MC TypeCast | MC 타캐 | Tessa | 테사 |\n| Gongchul | 공철 | Mia | 미아 | Tian | 티안 |\n| Gowoon | 고운 | Michael | 마이클 | Tim | 팀 |\n| Grace | 그레이스 | Mija | 오미자 | Tina | 티나 |\n| Graham | 그레이엄 | Mijin | 미진 | Toby | 토비 |\n| Gunseok | 건석 | Mikyung | 미경 | Tommy | 토미 |\n| Gunwoo | 건우 | Millie | 밀리 | Tyson | 타이슨 |\n| Guri | 구리 | Minchae | 민채 | UiChan | 의찬이 |\n| Gus | 거스 | Minji | 민지 | Uncle Hank | 행크 |\n| Ha Eun | 하은 | Minjoon | 민준 | Valerie | 밸러리 |\n| Haejun | 해준 | Minju | 민주 | Valkyrie | 발키리 |\n| Haerang | 해랑 | Minjung | 민정 | Vanessa | 바네사 |\n| Hailey | 헤일리 | Minsang | 민상 | Verna | 베르나 |\n| Hajun | 하준 | Minsu | 민수 | Victoria | 빅토리아 |\n| Hakchul | 학철 | Mio | 미오 | Viktor | 빅토르 |\n| Hamchu | 햄쮸 | Miran Choi | 최미란 | Vincent | 빈센트 |\n| Han Taesung Caster | 한태성 캐스터 | Mirine | 미리내 | Vivien | 비비안 |\n| Hana | 하나 | Miseon | 미선 | Wade | 웨이드 |\n| Hangyeol | 한결 | Miso | 미소 | Walter | 월터 |\n| Hanjun | 한준 | Mister Gop | 미스터 갑 | Wangkwon | 왕권 |\n| Hanna | 한나 | Monggun | 몽군 | Weather Reporter Sky | 하늘 캐스터 |\n| Hans | 한스 | Moonjung | 문정 | West | 웨스트 |\n| Hansol | 한솔 | Mooyeol | 무열 | Wonho | 원호 |\n| Hanyoung | 한영 | Morgan | 모건 | Wonkyung | 원경 |\n| Harin | 하린 | Moru | 머루 | Wonwoo | 원우 |\n| Harper | 하퍼 | Mrs. Claus | 미세스 클로스 | Wooju | 우주 |\n| Hayul | 하율 | Munseok | 문석 | Woosung | 우성 |\n| Heejoon | 희준 | Munsu | 문수 | Xavier | 자비에르 |\n| Helena | 헬레나 | Myeonghee | 명희 | Yeeun | 예은 |\n| Henry | 헨리 | Myungil | 명일 | Yejin | 예진 |\n| Homun Sim | 심호문 | Myungjoo | 명주 | Yejoon | 예준 |\n| Hoon | 훈 | Naeun | 나은 | YeLin | 예린 |\n| Hosik | 호식 | Najin | 나진 | Yena | 예나 |\n| Hosun | 호선 | Namjoon | 남준 | Yeonah | 연아 |\n| Hoyoung | 호영 | Nana | 나나 | Yeonggeol | 영걸 |\n| Hugh | 휴 | Nari | 개나리 | Yeonhwa | 연화 |\n| Hwimin | 휘민 | Nathan | 네이선 | Yeonja | 연자 |\n| Hyejung | 혜정 | Neel | 닐 | Yeonsuh | 연서 |\n| Hyelee | 혜리 | Neoguard | 네오가드 | Yeonwoo | 연우 |\n| Hyemin | 혜민 | News anchor Jinsub | 송진섭 앵커 | Yeseul | 예슬 |\n| Hyena | 혜나 | Newscaster John | 존 앵커 | Yongsik | 용식이 |\n| Hyeongjin | 형진 | Nia | 니아 | Yoonseo | 윤서 |\n| Hyera | 혜라 | Noa | 노아 | Yoonsung | 윤성 |\n| Hyesu | 홈쇼핑 혜수 | Noel | 노엘 | Younggil | 영길 |\n| Hyewon | 혜원 | Noeul | 노을 | Younghee | 영희 |\n| Hyun | 현 | Norah | 노라 | Younghwan | 영환 |\n| Hyunji | 현지 | Nova | 노바 | Youngji | 영지 |\n| Hyunjin | 현진 | Old radio | 레디오 | Youngkyu | 영규 |\n| Hyunju | 현주 | Oliver | 올리버 | Youngmok | 영목 |\n| Hyunkyung | 현경 | Olivia | 올리비아 | Yubin | 유빈 |\n| Hyunmin | 현민 | Oscar | 오스카 | Yujin | 유진 |\n| Hyunseung | 현승 | Owen | 오언 | Yumi | 유미 |\n| HyunWoo | 현우 | P-0150N | 포이즌 | Yumin | 유민 |\n| Ian | 이안 | Pacang | 파캉 | Yunbin | 윤빈 |\n| Icarus | 이카루스 | Paige | 페이지 | Yunjeong | 윤정 |\n| Igyeom | 이겸 | Pangpang | 팡팡 | Yura | 유라 |\n| Ijun | 이준 | Patricia | 패트리샤 | Yuri | 유리 |\n| Ilho | 일호 | Patrick | 패트릭 | Yuseong | 유성 |\n| Ina | 이나 | Peter | 피터 | Zoey | 조이 |\n| Inhwa | 인화 | Philip | 필립 | | |\n\n
\n" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "사람이 읽을 수 있는 보이스 이름" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입 — `original` 은 타입캐스트가 기본 제공하는 스톡 보이스, `custom` 은 사용자가 업로드/클론한 보이스입니다." } }, "description": "모델별로 그룹화된 감정과 향상된 메타데이터가 있는 V2 보이스 응답 모델" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대입니다." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별(`male` 또는 `female`)입니다." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "지원하는 TTS 모델과 감정 목록입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유 보이스 식별자입니다. 기본 보이스는 `tc_`, 커스텀 보이스는 `uc_`로 시작합니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "추천 사용 사례 목록입니다." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "`{\"eng\": \"Daejin\", \"kor\": \"대진\"}` 같은 다국어 보이스 이름입니다." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입(`original` 또는 `custom`)입니다." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "제공되는 경우 사용할 수 있는 미리듣기 오디오 URL입니다." } }, "description": "다국어 이름과 미리듣기 URL을 포함한 보이스 정보입니다." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "API 플랜.\n\n사용 가능한 값:\n- **free**: 제한된 크레딧의 무료 등급\n- **lite**: 중간 수준 크레딧의 라이트 플랜\n- **plus**: 높은 크레딧의 플러스 플랜\n- **custom**: 커스텀 엔터프라이즈 플랜" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "음성 합성에 사용할 TTS 모델 버전. 다양한 모델은 다양한 기능과 품질 수준을 제공합니다.\n\n사용 가능한 모델:\n- **ssfm-v30**: 향상된 플로우와 추가 감정 프리셋이 있는 최신 모델(권장)\n- **ssfm-v21**: 검증된 신뢰성과 일관된 품질을 갖춘 안정적인 모델\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS 모델 버전(예: ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "이 모델에서 지원되는 감정 목록" } }, "description": "버전 및 지원되는 감정을 포함한 모델 정보" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "영어 보이스 이름입니다." }, "kor": { "type": "string", "title": "Kor", "description": "한국어 보이스 이름입니다." } }, "description": "ISO 639-3 언어 코드를 키로 사용하는 다국어 보이스 이름입니다." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "보이스 타입 분류.\n\n- `original` — 타입캐스트가 기본 제공하는 스톡 보이스로, 모든 계정에서 사용 가능합니다.\n- `custom` — 사용자가 본인의 샘플을 업로드하거나 클로닝해서 만든 보이스입니다." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "성별 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **male**: 남성 보이스\n- **female**: 여성 보이스\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "음성 합성에 사용 가능한 감정 프리셋. 각 감정은 생성된 음성의 톤, 속도, 표현력에 영향을 줍니다.\n\n**ssfm-v21 지원 감정 (4종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n\n**ssfm-v30 지원 감정 (7종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n- whisper: 부드럽고 조용한 말\n- toneup: 더 높은 톤 강조\n- tonedown: 더 낮은 톤 강조\n\n/v2/voices API 응답을 통해 각 음성에 사용 가능한 감정을 확인하세요.\n" }, "SmartPrompt": { "type": "object", "title": "스마트 프롬프트 (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "TTSRequest의 `text` 필드 이후에 오는 텍스트. 감정 추론을 위한 전방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 전환을 예측하는 데 도움\r\n- 다음 컨텍스트가 없으면 비워 둡니다\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "프롬프트 유형을 식별하는 판별자 필드. 컨텍스트 인식 감정 추론을 위해 \"smart\"로 설정해야 합니다.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "TTSRequest의 `text` 필드 이전에 오는 텍스트. 감정 추론을 위한 후방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 빌드업과 컨텍스트를 이해하는 데 도움\r\n- 이전 컨텍스트가 없으면 비워 둡니다\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "프리셋 프롬프트 (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "프롬프트 유형을 식별하는 판별자 필드. 프리셋 기반 감정 제어를 위해 \"preset\"으로 설정해야 합니다.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "생성된 음성에 적용할 감정 프리셋.\r\n\r\n지원되는 감정: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "생성된 음성의 감정 표현 강도를 제어합니다.\r\n\r\n- 0.0: 완전히 중립적, 감정 색채 없음\r\n- 0.5: 미묘한 감정 힌트\r\n- 1.0: 표준 감정 표현(기본값)\r\n- 1.5: 강한 감정 강조\r\n- 2.0: 최대 강도, 매우 표현력 있음\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "콘텐츠 유형 필터링을 위한 보이스 사용 사례 카테고리. 각 보이스는 특정 콘텐츠 유형에 대한 적합성을 나타내는 하나 이상의 사용 사례로 태그됩니다.\n\n**사용 가능한 사용 사례:**\n- **Announcer**: 공공 발표 및 프레젠테이션\n- **Anime**: 애니메이션 보이스\n- **Audiobook**: 장문 내레이션 및 스토리텔링\n- **Conversational**: 챗봇 및 대화형 AI\n- **Documentary**: 다큐멘터리 내레이션 및 해설\n- **E-learning**: 교육 콘텐츠 및 튜토리얼\n- **Rapper**: 랩 및 음악 퍼포먼스\n- **Game**: 비디오 게임 보이스 및 내레이션\n- **Tiktok/Reels**: SNS 숏츠\n- **News**: 뉴스 방송\n- **Podcast**: 방송 및 팟캐스트 제작\n- **Voicemail**: IVR 시스템 및 음성 비서\n- **Ads**: 광고 및 홍보 콘텐츠\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "문제를 설명하는 오류 메시지" }, "message": { "type": "string", "description": "구조화된 오류에 대한 설명" }, "error_code": { "type": "string", "description": "구조화된 오류를 식별하는 코드" } }, "description": "기존 오류와 유효성 검사 오류는 `detail`을 사용하며, 구조화된 오류는 `error_code`와 `message`를 사용합니다." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "`status`가 `failed`일 때의 실패 사유입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "source": { "type": "string", "title": "Source", "description": "생성 방식(`instant` 또는 `professional`)입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "UTC 기준 생성 시각입니다." } }, "description": "인증된 계정이 소유한 커스텀 보이스입니다." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "추천 관련도 점수. 값이 높을수록 검색어와 더 잘 맞는 후보입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "`tc_` prefix 가 붙은 타입캐스트 보이스 식별자. 텍스트 음성 변환 요청의 `voice_id` 로 사용할 수 있습니다." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "사람이 읽을 수 있는 보이스 이름." } }, "description": "`GET /v1/voices/recommendations` 응답의 보이스 추천 후보. 관련도 순서로 정렬됩니다." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "커스텀 보이스의 현재 생성 또는 학습 상태입니다." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름 (1~30자)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "보이스가 클로닝된 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "`uc_` prefix 가 붙은 커스텀 보이스 식별자. `POST /v1/text-to-speech` 등 `voice_id` 를 받는 엔드포인트에 그대로 사용할 수 있습니다." } }, "description": "`POST /v1/voices/clone` 응답 — 퀵 클로닝으로 생성된 커스텀 보이스 메타데이터." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "세그먼트 구분값입니다. 항상 `pause`입니다." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "초 단위 쉼 길이입니다. 개별 쉼은 최대 10초, 모든 쉼의 합은 최대 60초입니다.", "exclusiveMinimum": 0 } }, "description": "크레딧 차감 없이 삽입되는 무음 구간입니다.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 단어 단위 정렬 구간." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "현재 구독 플랜명" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "사용 제한 정보" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "크레딧 사용 정보" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "구독 정보 응답 모델" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호/공백 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 문자 단위 정렬 구간." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." } }, "description": "퀵 클로닝 또는 프리미엄 클로닝 생성 요청 결과입니다." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "base64 로 인코딩된 오디오 바이트. `audio_format` 확장자로 디코딩해 파일로 저장할 수 있습니다." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "단어 단위 타임스탬프(문장부호 포함). 요청이 `granularity=char` 일 때는 `null`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "문자 단위 타임스탬프(문장부호와 공백 포함). 요청이 `granularity=word` 일 때는 `null`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "`audio` 필드의 오디오 인코딩 포맷 — `wav` 또는 `mp3` (요청의 `output.audio_format` 에 따라 결정)." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "생성된 오디오의 길이(초)." } }, "description": "TTS 생성 + 타임스탬프 정렬 통합 응답." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "오디오 샘플. WAV 또는 MP3, 최대 25MB, 5초 이상 150초 이하.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "보이스 이름 (1~30자)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "클로닝할 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." } }, "description": "퀵 클로닝 요청을 위한 multipart 본문." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "파일", "format": "binary", "description": "25 MiB 이하이면서 길이가 5초 이상 150초 이하인 WAV 또는 MP3 음성 파일 1개입니다.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "상품 내레이터", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." } }, "description": "퀵 클로닝용 multipart 요청 본문입니다." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "커스텀 보이스 이름", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "파일", "description": "학습에 사용할 WAV 또는 MP3 음성 파일입니다. 하나의 파일을 업로드할 수 있습니다.\r\n\r\n**업로드 제한**\r\n\r\n* WAV 또는 MP3 파일 1개\r\n* 파일 크기: 1 GiB 이하\r\n* 길이: 5분 이상 3시간 이하\r\n* 샘플레이트: 16 kHz 이상\r\n\r\n최상의 결과를 위해 다음 조건을 충족하는 오디오를 권장합니다.\r\n\r\n* 생성된 보이스가 재현하기를 원하는 말투와 최대한 가깝게 녹음해 주세요.\r\n* 배경 소음이 없는 조용한 환경에서 녹음해 주세요.\r\n* 한 명의 화자 목소리만 포함해 주세요.\r\n* `language` 필드에 지정한 언어로 녹음해 주세요.\r\n* 오디오 길이가 길수록 생성되는 보이스의 품질이 향상됩니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." }, "language": { "type": "string", "title": "Language", "example": "kor", "description": "`kor`, `eng` 같은 ISO 639-3 언어 코드입니다." } }, "description": "프리미엄 클로닝용 multipart 요청 본문입니다." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "인증을 위한 API 키. 타입캐스트 API 콘솔에서 API 키를 생성할 수 있습니다." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 퀵 클로닝 (/v1/voices/clone) > 짧은 오디오 샘플을 업로드해 커스텀 보이스를 생성하면, 이후 텍스트 음성 변환 호출에서 기본 보이스와 동일하게 사용할 수 있습니다. WAV 또는 MP3 파일(최대 25MB)을 업로드하면 서버가 speaker embedding 을 추출하여 `uc_` prefix 가 붙은 커스텀 보이스 ID 를 반환합니다. 이 ID 는 `POST /v1/text-to-speech` 의 `voice_id` 등 voice_id 를 받는 어떤 엔드포인트에도 그대로 전달할 수 있습니다. 원본 오디오는 응답 후 background 에서 S3 에 업로드됩니다. **제한 사항** - 오디오 파일: 최대 25MB. WAV 또는 MP3. - 오디오 길이: 5초 이상 150초 이하. - 보이스 이름: 1~30자. - 모델: `ssfm-v21` 또는 `ssfm-v30`. 클로닝된 보이스는 해당 엔진 모델에 묶입니다. - 플랜별로 동시에 보유 가능한 커스텀 보이스 수가 제한됩니다(`custom_voice_slot`). 슬롯이 가득 찼다면 `DELETE /v1/voices/{voice_id}` 로 비워주세요. **일반 사용 흐름** 1. `POST /v1/voices/clone` 으로 샘플 오디오 업로드 → `voice_id` 수신 (예: `uc_64a1b2...`). 2. `POST /v1/text-to-speech` 의 `voice_id` 에 클로닝된 ID 사용. 3. 더 이상 필요 없으면 `DELETE /v1/voices/{voice_id}` 로 삭제. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "프로덕션 서버" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/voices/clone": { "post": { "tags": [ "Deprecated" ], "x-mint": { "href": "/ko/api-reference/voices/instant-cloning" }, "summary": "퀵 클로닝 (/v1/voices/clone)", "responses": { "200": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CustomVoiceResponse" }, "example": { "name": "my-voice", "model": "ssfm-v30", "voice_id": "uc_64a1b2c3d4e5f6a7b8c9d0e1" } } }, "description": "Successful Response - Custom voice created" }, "400": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "examples": { "audio_too_long": { "value": { "detail": "Audio duration exceeds maximum" }, "summary": "Audio is longer than 150 seconds" }, "file_too_large": { "value": { "detail": "File size exceeds maximum limit" }, "summary": "File is too large" }, "audio_too_short": { "value": { "detail": "Audio duration is below minimum" }, "summary": "Audio is shorter than 5 seconds" }, "audio_unreadable": { "value": { "detail": "Failed to read audio metadata" }, "summary": "Audio metadata cannot be read" } } } }, "description": "Bad Request - Invalid audio file, file size, duration, or other validation error" }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Unauthorized - Invalid or missing API key" }, "403": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Voice cloning is not available on your plan" } } }, "description": "Forbidden - Voice cloning is not available on your plan" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid request format" } } }, "description": "Validation Error - Invalid request parameters" }, "429": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Too many requests" } } }, "description": "Too Many Requests - Rate limit or concurrency limit exceeded" } }, "deprecated": true, "description": "짧은 오디오 샘플을 업로드해 커스텀 보이스를 생성하면, 이후 텍스트 음성 변환 호출에서 기본 보이스와 동일하게 사용할 수 있습니다.\n\nWAV 또는 MP3 파일(최대 25MB)을 업로드하면 서버가 speaker embedding 을 추출하여 `uc_` prefix 가 붙은 커스텀 보이스 ID 를 반환합니다. 이 ID 는 `POST /v1/text-to-speech` 의 `voice_id` 등 voice_id 를 받는 어떤 엔드포인트에도 그대로 전달할 수 있습니다. 원본 오디오는 응답 후 background 에서 S3 에 업로드됩니다.\n\n**제한 사항**\n\n- 오디오 파일: 최대 25MB. WAV 또는 MP3.\n- 오디오 길이: 5초 이상 150초 이하.\n- 보이스 이름: 1~30자.\n- 모델: `ssfm-v21` 또는 `ssfm-v30`. 클로닝된 보이스는 해당 엔진 모델에 묶입니다.\n- 플랜별로 동시에 보유 가능한 커스텀 보이스 수가 제한됩니다(`custom_voice_slot`). 슬롯이 가득 찼다면 `DELETE /v1/voices/{voice_id}` 로 비워주세요.\n\n**일반 사용 흐름**\n\n1. `POST /v1/voices/clone` 으로 샘플 오디오 업로드 → `voice_id` 수신 (예: `uc_64a1b2...`).\n2. `POST /v1/text-to-speech` 의 `voice_id` 에 클로닝된 ID 사용.\n3. 더 이상 필요 없으면 `DELETE /v1/voices/{voice_id}` 로 삭제.", "operationId": "create_voice_clone_v1_voices_clone_post", "requestBody": { "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/Body_create_voice_clone_v1_voices_clone_post", "type": "object", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "format": "binary", "description": "오디오 샘플. WAV 또는 MP3, 최대 25MB, 5초 이상 150초 이하." }, "name": { "type": "string", "maxLength": 30, "minLength": 1, "description": "보이스 이름 (1~30자)." }, "model": { "enum": [ "ssfm-v21", "ssfm-v30" ], "type": "string", "description": "보이스를 클로닝할 엔진 모델." } } } } }, "required": true }, "x-codeSamples": [ { "lang": "cURL", "label": "cURL", "source": "curl --request POST \\\n --url 'https://api.typecast.ai/v1/voices/clone' \\\n --header 'X-API-KEY: ' \\\n -F 'file=@sample.wav' \\\n -F 'name=my-voice' \\\n -F 'model=ssfm-v30'\n" }, { "lang": "Python", "label": "Python (requests)", "source": "import requests\n\nwith open(\"sample.wav\", \"rb\") as f:\n response = requests.post(\n \"https://api.typecast.ai/v1/voices/clone\",\n headers={\"X-API-KEY\": \"\"},\n files={\"file\": (\"sample.wav\", f, \"audio/wav\")},\n data={\"name\": \"my-voice\", \"model\": \"ssfm-v30\"},\n )\n\nprint(response.json())\n# {\"voice_id\": \"uc_64a1b2c3d4e5f6a7b8c9d0e1\", \"name\": \"my-voice\", \"model\": \"ssfm-v30\"}\n" } ] } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "허용되는 최대 동시 요청 수" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "전체 커스텀 보이스 슬롯 한도 (professional 포함)" }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "사용 제한 정보" }, "Prompt": { "title": "프롬프트 (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "적용할 감정 프리셋.\r\n\r\nssfm-v21 지원 감정: normal, happy, sad, angry\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "example": 1, "description": "감정 표현 강도 제어(0.0~2.0).\r\n\r\n- 0.0: 완전히 중립적\r\n- 1.0: 표준 표현(기본값)\r\n- 2.0: 최대 강도\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "연령대 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **child**: 어린이 보이스(12세 미만)\n- **teenager**: 청소년 보이스(13-19세)\n- **young_adult**: 청년 보이스(20-35세)\n- **middle_age**: 중년 보이스(36-60세)\n- **elder**: 노년 보이스(60세 이상)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "플랜에서 기본으로 제공하는 총 크레딧" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "사용된 크레딧 수" } }, "description": "크레딧 사용 정보" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대 분류(어린이/청소년/청년/중년/노년)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별 분류(남성/여성)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "사용 가능한 감정이 있는 지원되는 TTS 모델 목록(예: [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유한 보이스 식별자. 기본 제공 보이스는 `tc_` prefix (예: `tc_60e5426de8b95f1d3000d7b5`), `POST /v1/voices/clone` 으로 만든 커스텀 보이스는 `uc_` prefix 를 사용합니다. 본인이 소유한 `uc_` 보이스도 `/v2/voices` 응답에 포함됩니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "이 보이스가 적합한 사용 사례 카테고리 목록\n\n## 보이스 이름 참고\n\n`voice_name` 필드는 영문으로만 반환됩니다. 아래 목록에서 한국어 표기를 확인하실 수 있습니다.\n\n
\n보이스 이름 목록 펼치기 (542개)\n\n| 영어 | 한국어 | 영어 | 한국어 | 영어 | 한국어 |\n| --- | --- | --- | --- | --- | --- |\n| Aaron | 에런 | Inhye | 인혜 | Piljae | 필재 |\n| Abigail | 아비게일 | Inseong | 인성 | Pyeonghwa | 평화 |\n| Ae-ran | 애란 | Instructor Han | 한유격 교관 | Rachel | 레이첼 |\n| Ael | 아엘 | Insun | 인선 | Ravi | 라비 |\n| Agatha | 아가사 | Jabbaba | 자바바 | Rayeon | 라연 |\n| Aiden | 에이든 | Jack | 잭 | Rebecca | 레베카 |\n| Alena | 알레나 | Jack-o’-Lantern | 잭오랜턴 | Reporter Bona | 다보나 기자 |\n| Alex | 알렉스 | Jackson | 잭슨 | Reporter Catalina | 카탈리나 기자 |\n| Amber | 엠버 | Jaeho | 재호 | Reporter Jinsub | 송진섭 기자 |\n| Anchor Hwa | 김경화 앵커 | Jaehun | 재훈 | Reporter Kang | 강수정 기자 |\n| Angela | 안젤라 | Jaejun | 재준 | Reporter Lee | 이승주 기자 |\n| Anja | 안냐 | Jaekyung | 재경 | Reporter Parkchan | 박찬 기자 |\n| Annabelle the Ghost | 귀신 애나벨 | Jaesun | 재선 | Rex | 렉스 |\n| Annie | 애니 | JaeYi | 재이 | Riley | 라일리 |\n| April | 에이프릴 | Jaeyoung | 재영 | Risan Ji | 지리산 |\n| Arang | 아랑 | Jain | 자인 | Rita | 리타 |\n| Ari | 아리 | Jake | 제이크 | River | 리버 |\n| Arin | 아린 | Janet | 재닛 | Robert | 로버트 |\n| Athena | 아테나 | Jangho | 장호 | Robo | 로보 |\n| Audrey | 오드리 | Jangwoon | 장운 | Romi | 로미 |\n| Avery | 에이버리 | Jechan | 제찬 | Ron | 론 |\n| Avong | 아봉 | Jeff | 제프 | Roro | 로로 |\n| Azzi | 아찌 | Jenna | 제나 | Rowoon | 로운 |\n| Babilon | 바빌론 | Jennifer | 제니퍼 | Royce | 로이스 |\n| Bell | 벨 | Jeong Choi | 최정 | Rudolph | 루돌프 |\n| Ben | 벤 | Jeongah | 정아 | Ruri | 루리 |\n| Benny | 베니 | JeongJin | 정진 | Rusty | 러스티 |\n| Beri | 베리 | Jeongseob | 정섭 | Ryan | 라이언 |\n| Billie | 빌리 | Jerome | 제롬 | Ryueun | 류은 |\n| Bomi | 이보미 | Jewby | 쥬비 | Sabrina the Witch | 마녀 사브리나 |\n| Bonggyu | 봉규 | Jian | 지안 | Saeron | 새론 |\n| Bongman Kim | 김봉만 | Jicheol | 지철 | Salty Chan-gu | 삐뚤어진 찬구 |\n| Bono | 보노 | Jihee | 지희 | Samantha | 사만다 |\n| Bora | 보라 | Jihoon | 지훈 | Sammy | 새미 |\n| Brad | 브래드 | Jihyun | 지현 | Sangdo | 상도 |\n| Bruce | 브루스 | Jimmy | 지미 | Sanghoon | 상훈 |\n| Buddy | 버디 | Jin | 진 | Sangwoo | 상우 |\n| Bumsoo | 범수 | Jinhan | 진한 | Santa | 산타 |\n| Buttaguy | 빠다가이 | Jinhang Kim | 김진행 | Santa Claus | 산타클로스 |\n| Caitlyn | 케이틀린 | Jinhee | 진희 | Santa Reporter | VJ 싼타 |\n| Callan | 칼란 | Jinhyuk | 진혁 | Sara | 세라 |\n| Camila | 카밀라 | Jinseo | 진서 | Sean | 션 |\n| Captain Bill | 캡틴 빌 | Jinung | 진웅 | SeHee | 세희 |\n| Carl | 칼 | Jinwoo | 진우 | Seheon | 세헌 |\n| Carlos | 카를로스 | Jiwoo | 지우 | Sejin | 세진 |\n| Carol | 캐롤 | Jiyoon | 지윤 | Sena | 세나 |\n| Catherine | 캐서린 | Jiyoung | 지영 | Seohee | 서희 |\n| Chad | 채드 | Jodie | 조디 | Seojin | 서진 |\n| Chaeah | 채아 | Jolly | 졸리 | Seojoon | 서준 |\n| Chan-gu | 찬구 | Jongdae | 종대 | Seok Choi | 최석 |\n| Changbae | 창배 | Jooeun | 주은 | SeokPil | 한석필 |\n| Changhee | 창희 | Joonghyun | 중현 | Seokpyo | 석표 |\n| Changmin | 창민 | Joongsik | 중식 | Seolhwa | 설화 |\n| Changsu | 박창수 | Joonkyu | 준규 | Seonha | 선하 |\n| Chanhyuk | 찬혁 | Joshua | 조슈아 | Seoyeon | 서연 |\n| Charlotte | 샬롯 | Juha | 주하 | Seoyoon | 서윤 |\n| Chase | 체이스 | Juho | 주호 | Seungah | 승아 |\n| Cheolhoon | 철훈 | Jungbong | 정봉 | Seungheon | 승헌 |\n| Cheolyong | 리철용 | Junghee | 정희 | Seungho | 승호 |\n| Cherry | 체리 | Jungjae | 정재 | Seunghwa | 승화 |\n| Chester | 체스터 | Jungmin | 정민 | Seungjae | 승재 |\n| Chiho | 치호 | Jungseok | 정석 | Seungmoon | 승문 |\n| Chloe | 클로이 | Jungsoon | 정순 | Seungwon | 승원 |\n| Choyeon | 강초연 교관 | Jungwon | 정원 | Seungyeon | 승연 |\n| Chungah | 청아 | Junhee | 준희 | Sewoo | 세우 |\n| Chunsik Kang | 강춘식 기자 | Junho | 준호 | Shana | 샤나 |\n| Claire | 클레어 | JunKi | 준기 | Sheriff Kim | 김반장 |\n| Classic Narrator | 미스타 변사 | Junsang | 준상 | Shinhe | 신혜 |\n| Cole | 콜 | Junseong | 준성 | Shinwook | 신욱 |\n| Cox | 콕스 | Justice Roh | 정의로 | Shotgun | 샷건 |\n| Cyrus | 사이러스 | Justin | 저스틴 | Simon | 사이먼 |\n| Dabin | 다빈 | Juwon | 주원 | Sindarin | 신다린 |\n| Daegil | 대길 | Juyoung | 주영 | Sio | 시오 |\n| Dahee | 다희 | K-Santa | 산타 할아버지 | Siwon | 시원 |\n| Dahyeon | 다현 | Kangil | 강일 | Siwoo | 시우 |\n| Damian | 데이미언 | Kanno | 칸노 | Siyeon | 시연 |\n| Dan | 댄 | Katie | 케이티 | Skylar | 스카일러 |\n| Dana | 다나 | Kelly | 켈리 | Slushy | 슬러시 |\n| Dasom | 다솜 | Kelsey | 켈시 | Smoke | 스모크 |\n| David | 데이비드 | Kevin | 케빈 | Sohye | 소혜 |\n| Dean | 딘 | Keybo | 키보 | Sojin | 소진 |\n| Deokhwan | 덕환 | Kijang Kim | 김기장 | Soobin | 수빈 |\n| Dohan | 도한 | Killian the Vampire | 뱀파이어 킬리언 | Sookhee | 숙희 |\n| Dohee | 도희 | Kiseob | 기섭 | Sooni | 순이 |\n| Dohyun | 도현 | Klip Kim | 클립 킴 | Sophia | 소피아 |\n| Dollar Jr. | 달러 주니어 | Koombo | 쿰보 | Sora | 소라 |\n| Doug | 더그 | Kristen | 크리스틴 | Soye | 소예 |\n| Doughnut | 도넛 | Kukhee | 국희 | Soyi | 소이 |\n| DU5T | 더스트 | Kwonil | 권일 | Soyoung | 소영 |\n| Duckchun | 덕춘 | Kyumin | 규민 | Soyul | 소율 |\n| Duke | 듀크 | Kyungho | 경호 | Spice | 스파이스 |\n| Dukgu | 덕구 | Kyungsoo | 경수 | Sportscaster Kang | 강수정 캐스터 |\n| Duman | 두만 | Kyungsook | 경숙 | Sportscaster Tony | 이영광 캐스터 |\n| Dupil | 곽두필 | Lady Cho | 단희빈 | Starling | 스탈링 |\n| DVZY | 데이지 | Lala | 라라 | Stephanie | 스테파니 |\n| Dylan | 딜런 | Lamie | 라미 | Storyteller Jinsub | 송진섭 스토리텔러 |\n| E-seller Sherri | 쇼린이 | Landon | 랜던 | Sua | 수아 |\n| Echo | 에코 | Larry | 래리 | Suho | 수호 |\n| Edward | 에드워드 | Leo | 레오 | Suji | 수지 |\n| Elias | 엘리아스 | Liam | 리암 | Sujin | 수진 |\n| Elise | 엘리스 | Lindsay | 린지 | Sullock Hong | 홍설록 |\n| Ella | 엘라 | Liz | 리즈 | Sumin | 수민 |\n| Eman | 이만 | Lloyd | 로이드 | Sungbae | 성배 |\n| Emma | 엠마 | Logan | 로건 | Sunggyu | 성규 |\n| Eunbin | 은빈 | Lucille | 루실 | Sungho | 성호 |\n| Eunchae | 은채 | Luna | 루나 | Sunghoon | 성훈 |\n| Eunha | 은하 | Lydia | 리디아 | Sunghyun | 성현 |\n| Eunsol | 은솔 | Maddie | 매디 | Sungjun | 홈쇼핑 성준 |\n| Frankenstein | 프랑켄슈타인 | Maisie | 메이시 | Sungkwon | 성권 |\n| Furnando | 터르난도 | Margaret | 마가렛 | Sungtae | 성태 |\n| Gaeul | 가을 | Margot | 마고 | Sungwook | 성욱 |\n| Gahee | 가희 | Matthew | 매튜 | Sunyoung | 선영 |\n| George | 조지 | MBTI EF (F) | MBTI EF 여 | Suyoon | 수윤 |\n| GeumHee | 금희 | MBTI EF (M) | MBTI EF 남 | Sylvia | 실비아 |\n| Geunhyeok | 근혁 | MBTI ET (F) | MBTI ET 여 | Taebaek | 장태백 |\n| Geunseok | 근석 | MBTI ET (M) | MBTI ET 남 | Taeji | 태지 |\n| Geunwoo | 근우 | MBTI IF (F) | MBTI IF 여 | Taejoong | 태중 |\n| Geunyeong | 근영 | MBTI IF (M) | MBTI IF 남 | Taemin | 태민 |\n| Ggami | 까미 | MBTI IT (F) | MBTI IT 여 | Taesub | 태섭 |\n| Ggomi | 꼬미 | MBTI IT (M) | MBTI IT 남 | Taewoo | 태우 |\n| Glenda | 글렌다 | MC Kong | MC콩 | Teal | 틸 |\n| Goat Kim | 김고트 | MC TypeCast | MC 타캐 | Tessa | 테사 |\n| Gongchul | 공철 | Mia | 미아 | Tian | 티안 |\n| Gowoon | 고운 | Michael | 마이클 | Tim | 팀 |\n| Grace | 그레이스 | Mija | 오미자 | Tina | 티나 |\n| Graham | 그레이엄 | Mijin | 미진 | Toby | 토비 |\n| Gunseok | 건석 | Mikyung | 미경 | Tommy | 토미 |\n| Gunwoo | 건우 | Millie | 밀리 | Tyson | 타이슨 |\n| Guri | 구리 | Minchae | 민채 | UiChan | 의찬이 |\n| Gus | 거스 | Minji | 민지 | Uncle Hank | 행크 |\n| Ha Eun | 하은 | Minjoon | 민준 | Valerie | 밸러리 |\n| Haejun | 해준 | Minju | 민주 | Valkyrie | 발키리 |\n| Haerang | 해랑 | Minjung | 민정 | Vanessa | 바네사 |\n| Hailey | 헤일리 | Minsang | 민상 | Verna | 베르나 |\n| Hajun | 하준 | Minsu | 민수 | Victoria | 빅토리아 |\n| Hakchul | 학철 | Mio | 미오 | Viktor | 빅토르 |\n| Hamchu | 햄쮸 | Miran Choi | 최미란 | Vincent | 빈센트 |\n| Han Taesung Caster | 한태성 캐스터 | Mirine | 미리내 | Vivien | 비비안 |\n| Hana | 하나 | Miseon | 미선 | Wade | 웨이드 |\n| Hangyeol | 한결 | Miso | 미소 | Walter | 월터 |\n| Hanjun | 한준 | Mister Gop | 미스터 갑 | Wangkwon | 왕권 |\n| Hanna | 한나 | Monggun | 몽군 | Weather Reporter Sky | 하늘 캐스터 |\n| Hans | 한스 | Moonjung | 문정 | West | 웨스트 |\n| Hansol | 한솔 | Mooyeol | 무열 | Wonho | 원호 |\n| Hanyoung | 한영 | Morgan | 모건 | Wonkyung | 원경 |\n| Harin | 하린 | Moru | 머루 | Wonwoo | 원우 |\n| Harper | 하퍼 | Mrs. Claus | 미세스 클로스 | Wooju | 우주 |\n| Hayul | 하율 | Munseok | 문석 | Woosung | 우성 |\n| Heejoon | 희준 | Munsu | 문수 | Xavier | 자비에르 |\n| Helena | 헬레나 | Myeonghee | 명희 | Yeeun | 예은 |\n| Henry | 헨리 | Myungil | 명일 | Yejin | 예진 |\n| Homun Sim | 심호문 | Myungjoo | 명주 | Yejoon | 예준 |\n| Hoon | 훈 | Naeun | 나은 | YeLin | 예린 |\n| Hosik | 호식 | Najin | 나진 | Yena | 예나 |\n| Hosun | 호선 | Namjoon | 남준 | Yeonah | 연아 |\n| Hoyoung | 호영 | Nana | 나나 | Yeonggeol | 영걸 |\n| Hugh | 휴 | Nari | 개나리 | Yeonhwa | 연화 |\n| Hwimin | 휘민 | Nathan | 네이선 | Yeonja | 연자 |\n| Hyejung | 혜정 | Neel | 닐 | Yeonsuh | 연서 |\n| Hyelee | 혜리 | Neoguard | 네오가드 | Yeonwoo | 연우 |\n| Hyemin | 혜민 | News anchor Jinsub | 송진섭 앵커 | Yeseul | 예슬 |\n| Hyena | 혜나 | Newscaster John | 존 앵커 | Yongsik | 용식이 |\n| Hyeongjin | 형진 | Nia | 니아 | Yoonseo | 윤서 |\n| Hyera | 혜라 | Noa | 노아 | Yoonsung | 윤성 |\n| Hyesu | 홈쇼핑 혜수 | Noel | 노엘 | Younggil | 영길 |\n| Hyewon | 혜원 | Noeul | 노을 | Younghee | 영희 |\n| Hyun | 현 | Norah | 노라 | Younghwan | 영환 |\n| Hyunji | 현지 | Nova | 노바 | Youngji | 영지 |\n| Hyunjin | 현진 | Old radio | 레디오 | Youngkyu | 영규 |\n| Hyunju | 현주 | Oliver | 올리버 | Youngmok | 영목 |\n| Hyunkyung | 현경 | Olivia | 올리비아 | Yubin | 유빈 |\n| Hyunmin | 현민 | Oscar | 오스카 | Yujin | 유진 |\n| Hyunseung | 현승 | Owen | 오언 | Yumi | 유미 |\n| HyunWoo | 현우 | P-0150N | 포이즌 | Yumin | 유민 |\n| Ian | 이안 | Pacang | 파캉 | Yunbin | 윤빈 |\n| Icarus | 이카루스 | Paige | 페이지 | Yunjeong | 윤정 |\n| Igyeom | 이겸 | Pangpang | 팡팡 | Yura | 유라 |\n| Ijun | 이준 | Patricia | 패트리샤 | Yuri | 유리 |\n| Ilho | 일호 | Patrick | 패트릭 | Yuseong | 유성 |\n| Ina | 이나 | Peter | 피터 | Zoey | 조이 |\n| Inhwa | 인화 | Philip | 필립 | | |\n\n
\n" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "사람이 읽을 수 있는 보이스 이름" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입 — `original` 은 타입캐스트가 기본 제공하는 스톡 보이스, `custom` 은 사용자가 업로드/클론한 보이스입니다." } }, "description": "모델별로 그룹화된 감정과 향상된 메타데이터가 있는 V2 보이스 응답 모델" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대입니다." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별(`male` 또는 `female`)입니다." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "지원하는 TTS 모델과 감정 목록입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유 보이스 식별자입니다. 기본 보이스는 `tc_`, 커스텀 보이스는 `uc_`로 시작합니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "추천 사용 사례 목록입니다." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "`{\"eng\": \"Daejin\", \"kor\": \"대진\"}` 같은 다국어 보이스 이름입니다." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입(`original` 또는 `custom`)입니다." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "제공되는 경우 사용할 수 있는 미리듣기 오디오 URL입니다." } }, "description": "다국어 이름과 미리듣기 URL을 포함한 보이스 정보입니다." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "API 플랜.\n\n사용 가능한 값:\n- **free**: 제한된 크레딧의 무료 등급\n- **lite**: 중간 수준 크레딧의 라이트 플랜\n- **plus**: 높은 크레딧의 플러스 플랜\n- **custom**: 커스텀 엔터프라이즈 플랜" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "음성 합성에 사용할 TTS 모델 버전. 다양한 모델은 다양한 기능과 품질 수준을 제공합니다.\n\n사용 가능한 모델:\n- **ssfm-v30**: 향상된 플로우와 추가 감정 프리셋이 있는 최신 모델(권장)\n- **ssfm-v21**: 검증된 신뢰성과 일관된 품질을 갖춘 안정적인 모델\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS 모델 버전(예: ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "이 모델에서 지원되는 감정 목록" } }, "description": "버전 및 지원되는 감정을 포함한 모델 정보" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "영어 보이스 이름입니다." }, "kor": { "type": "string", "title": "Kor", "description": "한국어 보이스 이름입니다." } }, "description": "ISO 639-3 언어 코드를 키로 사용하는 다국어 보이스 이름입니다." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "보이스 타입 분류.\n\n- `original` — 타입캐스트가 기본 제공하는 스톡 보이스로, 모든 계정에서 사용 가능합니다.\n- `custom` — 사용자가 본인의 샘플을 업로드하거나 클로닝해서 만든 보이스입니다." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "성별 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **male**: 남성 보이스\n- **female**: 여성 보이스\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "음성 합성에 사용 가능한 감정 프리셋. 각 감정은 생성된 음성의 톤, 속도, 표현력에 영향을 줍니다.\n\n**ssfm-v21 지원 감정 (4종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n\n**ssfm-v30 지원 감정 (7종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n- whisper: 부드럽고 조용한 말\n- toneup: 더 높은 톤 강조\n- tonedown: 더 낮은 톤 강조\n\n/v2/voices API 응답을 통해 각 음성에 사용 가능한 감정을 확인하세요.\n" }, "SmartPrompt": { "type": "object", "title": "스마트 프롬프트 (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "TTSRequest의 `text` 필드 이후에 오는 텍스트. 감정 추론을 위한 전방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 전환을 예측하는 데 도움\r\n- 다음 컨텍스트가 없으면 비워 둡니다\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "프롬프트 유형을 식별하는 판별자 필드. 컨텍스트 인식 감정 추론을 위해 \"smart\"로 설정해야 합니다.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "TTSRequest의 `text` 필드 이전에 오는 텍스트. 감정 추론을 위한 후방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 빌드업과 컨텍스트를 이해하는 데 도움\r\n- 이전 컨텍스트가 없으면 비워 둡니다\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "프리셋 프롬프트 (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "프롬프트 유형을 식별하는 판별자 필드. 프리셋 기반 감정 제어를 위해 \"preset\"으로 설정해야 합니다.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "생성된 음성에 적용할 감정 프리셋.\r\n\r\n지원되는 감정: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "생성된 음성의 감정 표현 강도를 제어합니다.\r\n\r\n- 0.0: 완전히 중립적, 감정 색채 없음\r\n- 0.5: 미묘한 감정 힌트\r\n- 1.0: 표준 감정 표현(기본값)\r\n- 1.5: 강한 감정 강조\r\n- 2.0: 최대 강도, 매우 표현력 있음\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "콘텐츠 유형 필터링을 위한 보이스 사용 사례 카테고리. 각 보이스는 특정 콘텐츠 유형에 대한 적합성을 나타내는 하나 이상의 사용 사례로 태그됩니다.\n\n**사용 가능한 사용 사례:**\n- **Announcer**: 공공 발표 및 프레젠테이션\n- **Anime**: 애니메이션 보이스\n- **Audiobook**: 장문 내레이션 및 스토리텔링\n- **Conversational**: 챗봇 및 대화형 AI\n- **Documentary**: 다큐멘터리 내레이션 및 해설\n- **E-learning**: 교육 콘텐츠 및 튜토리얼\n- **Rapper**: 랩 및 음악 퍼포먼스\n- **Game**: 비디오 게임 보이스 및 내레이션\n- **Tiktok/Reels**: SNS 숏츠\n- **News**: 뉴스 방송\n- **Podcast**: 방송 및 팟캐스트 제작\n- **Voicemail**: IVR 시스템 및 음성 비서\n- **Ads**: 광고 및 홍보 콘텐츠\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "문제를 설명하는 오류 메시지" }, "message": { "type": "string", "description": "구조화된 오류에 대한 설명" }, "error_code": { "type": "string", "description": "구조화된 오류를 식별하는 코드" } }, "description": "기존 오류와 유효성 검사 오류는 `detail`을 사용하며, 구조화된 오류는 `error_code`와 `message`를 사용합니다." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "`status`가 `failed`일 때의 실패 사유입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "source": { "type": "string", "title": "Source", "description": "생성 방식(`instant` 또는 `professional`)입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "UTC 기준 생성 시각입니다." } }, "description": "인증된 계정이 소유한 커스텀 보이스입니다." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "추천 관련도 점수. 값이 높을수록 검색어와 더 잘 맞는 후보입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "`tc_` prefix 가 붙은 타입캐스트 보이스 식별자. 텍스트 음성 변환 요청의 `voice_id` 로 사용할 수 있습니다." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "사람이 읽을 수 있는 보이스 이름." } }, "description": "`GET /v1/voices/recommendations` 응답의 보이스 추천 후보. 관련도 순서로 정렬됩니다." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "커스텀 보이스의 현재 생성 또는 학습 상태입니다." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름 (1~30자)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "보이스가 클로닝된 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "`uc_` prefix 가 붙은 커스텀 보이스 식별자. `POST /v1/text-to-speech` 등 `voice_id` 를 받는 엔드포인트에 그대로 사용할 수 있습니다." } }, "description": "`POST /v1/voices/clone` 응답 — 퀵 클로닝으로 생성된 커스텀 보이스 메타데이터." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "세그먼트 구분값입니다. 항상 `pause`입니다." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "초 단위 쉼 길이입니다. 개별 쉼은 최대 10초, 모든 쉼의 합은 최대 60초입니다.", "exclusiveMinimum": 0 } }, "description": "크레딧 차감 없이 삽입되는 무음 구간입니다.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 단어 단위 정렬 구간." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "현재 구독 플랜명" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "사용 제한 정보" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "크레딧 사용 정보" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "구독 정보 응답 모델" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호/공백 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 문자 단위 정렬 구간." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." } }, "description": "퀵 클로닝 또는 프리미엄 클로닝 생성 요청 결과입니다." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "base64 로 인코딩된 오디오 바이트. `audio_format` 확장자로 디코딩해 파일로 저장할 수 있습니다." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "단어 단위 타임스탬프(문장부호 포함). 요청이 `granularity=char` 일 때는 `null`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "문자 단위 타임스탬프(문장부호와 공백 포함). 요청이 `granularity=word` 일 때는 `null`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "`audio` 필드의 오디오 인코딩 포맷 — `wav` 또는 `mp3` (요청의 `output.audio_format` 에 따라 결정)." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "생성된 오디오의 길이(초)." } }, "description": "TTS 생성 + 타임스탬프 정렬 통합 응답." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "오디오 샘플. WAV 또는 MP3, 최대 25MB, 5초 이상 150초 이하.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "보이스 이름 (1~30자)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "클로닝할 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." } }, "description": "퀵 클로닝 요청을 위한 multipart 본문." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "파일", "format": "binary", "description": "25 MiB 이하이면서 길이가 5초 이상 150초 이하인 WAV 또는 MP3 음성 파일 1개입니다.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "상품 내레이터", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." } }, "description": "퀵 클로닝용 multipart 요청 본문입니다." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "커스텀 보이스 이름", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "파일", "description": "학습에 사용할 WAV 또는 MP3 음성 파일입니다. 하나의 파일을 업로드할 수 있습니다.\r\n\r\n**업로드 제한**\r\n\r\n* WAV 또는 MP3 파일 1개\r\n* 파일 크기: 1 GiB 이하\r\n* 길이: 5분 이상 3시간 이하\r\n* 샘플레이트: 16 kHz 이상\r\n\r\n최상의 결과를 위해 다음 조건을 충족하는 오디오를 권장합니다.\r\n\r\n* 생성된 보이스가 재현하기를 원하는 말투와 최대한 가깝게 녹음해 주세요.\r\n* 배경 소음이 없는 조용한 환경에서 녹음해 주세요.\r\n* 한 명의 화자 목소리만 포함해 주세요.\r\n* `language` 필드에 지정한 언어로 녹음해 주세요.\r\n* 오디오 길이가 길수록 생성되는 보이스의 품질이 향상됩니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." }, "language": { "type": "string", "title": "Language", "example": "kor", "description": "`kor`, `eng` 같은 ISO 639-3 언어 코드입니다." } }, "description": "프리미엄 클로닝용 multipart 요청 본문입니다." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "인증을 위한 API 키. 타입캐스트 API 콘솔에서 API 키를 생성할 수 있습니다." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ``` --- > ## Documentation Index > Fetch the complete documentation index at: https://typecast.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # 커스텀 보이스 삭제 (/v1/voices/{voice_id}) > `POST /v1/voices/clone` 으로 생성한 커스텀 보이스를 soft delete 합니다. 활성 보이스 목록에서 제거되며 `custom_voice_slot` 한 자리가 즉시 회복되어 새 클로닝에 사용할 수 있습니다. 삭제 후 같은 `voice_id` 로 요청하면 404 가 반환됩니다. 본인이 소유한 보이스만 삭제 가능하며, 타인의 보이스를 삭제하려 하면 404 가 반환됩니다. 성공 시 204 No Content 를 반환합니다. ## OpenAPI ```json { "openapi": "3.1.0", "info": { "title": "Typecast API", "x-logo": { "url": "https://typecast.ai/_ipx/_/image/logo/tc_logo.webp" }, "version": "0.1.2" }, "servers": [ { "url": "https://api.typecast.ai", "description": "프로덕션 서버" } ], "security": [ { "ApiKeyAuth": [] } ], "paths": { "/v1/voices/{voice_id}": { "delete": { "tags": [ "Deprecated" ], "x-mint": { "href": "/ko/api-reference/voices/delete-custom-voice" }, "summary": "커스텀 보이스 삭제 (/v1/voices/{voice_id})", "responses": { "204": { "description": "No Content - Voice deleted successfully. The response body is intentionally empty per RFC 9110; treat any 2xx status as success." }, "401": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid API key" } } }, "description": "Unauthorized - Invalid or missing API key" }, "404": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Voice not found" } } }, "description": "Not Found - Voice does not exist or is not owned by the caller" }, "422": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" }, "example": { "detail": "Invalid request format" } } }, "description": "Validation Error - Invalid voice_id format" } }, "deprecated": true, "parameters": [ { "in": "path", "name": "voice_id", "schema": { "type": "string", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "pattern": "^uc_[A-Za-z0-9]+$" }, "required": true, "description": "`uc_` prefix 가 붙은 커스텀 보이스 식별자." } ], "description": "`POST /v1/voices/clone` 으로 생성한 커스텀 보이스를 soft delete 합니다. 활성 보이스 목록에서 제거되며 `custom_voice_slot` 한 자리가 즉시 회복되어 새 클로닝에 사용할 수 있습니다.\n\n삭제 후 같은 `voice_id` 로 요청하면 404 가 반환됩니다. 본인이 소유한 보이스만 삭제 가능하며, 타인의 보이스를 삭제하려 하면 404 가 반환됩니다.\n\n성공 시 204 No Content 를 반환합니다.", "operationId": "delete_custom_voice_v1_voices__voice_id__delete" } } }, "components": { "schemas": { "Limits": { "type": "object", "title": "Limits", "required": [ "concurrency_limit" ], "properties": { "concurrency_limit": { "type": "integer", "title": "Concurrency Limit", "description": "허용되는 최대 동시 요청 수" }, "custom_voice_slot": { "type": "integer", "title": "Custom Voice Slot", "default": 0, "minimum": 0, "description": "전체 커스텀 보이스 슬롯 한도 (professional 포함)" }, "professional_voice_slot": { "type": "integer", "title": "Professional Voice Slot", "default": 0, "minimum": 0, "description": "프리미엄 클로닝 보이스 슬롯 한도" } }, "description": "사용 제한 정보" }, "Prompt": { "title": "프롬프트 (ssfm-v21)", "properties": { "emotion_preset": { "example": "normal", "description": "적용할 감정 프리셋.\r\n\r\nssfm-v21 지원 감정: normal, happy, sad, angry\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "example": 1, "description": "감정 표현 강도 제어(0.0~2.0).\r\n\r\n- 0.0: 완전히 중립적\r\n- 1.0: 표준 표현(기본값)\r\n- 2.0: 최대 강도\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정." }, "AgeEnum": { "enum": [ "child", "teenager", "young_adult", "middle_age", "elder" ], "type": "string", "title": "AgeEnum", "description": "연령대 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **child**: 어린이 보이스(12세 미만)\n- **teenager**: 청소년 보이스(13-19세)\n- **young_adult**: 청년 보이스(20-35세)\n- **middle_age**: 중년 보이스(36-60세)\n- **elder**: 노년 보이스(60세 이상)\n" }, "Credits": { "type": "object", "title": "Credits", "required": [ "plan_credits", "used_credits" ], "properties": { "plan_credits": { "type": "integer", "title": "Plan Credits", "description": "플랜에서 기본으로 제공하는 총 크레딧" }, "used_credits": { "type": "integer", "title": "Used Credits", "description": "사용된 크레딧 수" } }, "description": "크레딧 사용 정보" }, "VoiceV2": { "type": "object", "title": "VoiceV2", "required": [ "voice_id", "voice_name", "models", "voice_type" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대 분류(어린이/청소년/청년/중년/노년)" }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별 분류(남성/여성)" }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "사용 가능한 감정이 있는 지원되는 TTS 모델 목록(예: [{'version': 'ssfm-v21', 'emotions': ['happy', 'sad']}])" }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유한 보이스 식별자. 기본 제공 보이스는 `tc_` prefix (예: `tc_60e5426de8b95f1d3000d7b5`), `POST /v1/voices/clone` 으로 만든 커스텀 보이스는 `uc_` prefix 를 사용합니다. 본인이 소유한 `uc_` 보이스도 `/v2/voices` 응답에 포함됩니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "이 보이스가 적합한 사용 사례 카테고리 목록\n\n## 보이스 이름 참고\n\n`voice_name` 필드는 영문으로만 반환됩니다. 아래 목록에서 한국어 표기를 확인하실 수 있습니다.\n\n
\n보이스 이름 목록 펼치기 (542개)\n\n| 영어 | 한국어 | 영어 | 한국어 | 영어 | 한국어 |\n| --- | --- | --- | --- | --- | --- |\n| Aaron | 에런 | Inhye | 인혜 | Piljae | 필재 |\n| Abigail | 아비게일 | Inseong | 인성 | Pyeonghwa | 평화 |\n| Ae-ran | 애란 | Instructor Han | 한유격 교관 | Rachel | 레이첼 |\n| Ael | 아엘 | Insun | 인선 | Ravi | 라비 |\n| Agatha | 아가사 | Jabbaba | 자바바 | Rayeon | 라연 |\n| Aiden | 에이든 | Jack | 잭 | Rebecca | 레베카 |\n| Alena | 알레나 | Jack-o’-Lantern | 잭오랜턴 | Reporter Bona | 다보나 기자 |\n| Alex | 알렉스 | Jackson | 잭슨 | Reporter Catalina | 카탈리나 기자 |\n| Amber | 엠버 | Jaeho | 재호 | Reporter Jinsub | 송진섭 기자 |\n| Anchor Hwa | 김경화 앵커 | Jaehun | 재훈 | Reporter Kang | 강수정 기자 |\n| Angela | 안젤라 | Jaejun | 재준 | Reporter Lee | 이승주 기자 |\n| Anja | 안냐 | Jaekyung | 재경 | Reporter Parkchan | 박찬 기자 |\n| Annabelle the Ghost | 귀신 애나벨 | Jaesun | 재선 | Rex | 렉스 |\n| Annie | 애니 | JaeYi | 재이 | Riley | 라일리 |\n| April | 에이프릴 | Jaeyoung | 재영 | Risan Ji | 지리산 |\n| Arang | 아랑 | Jain | 자인 | Rita | 리타 |\n| Ari | 아리 | Jake | 제이크 | River | 리버 |\n| Arin | 아린 | Janet | 재닛 | Robert | 로버트 |\n| Athena | 아테나 | Jangho | 장호 | Robo | 로보 |\n| Audrey | 오드리 | Jangwoon | 장운 | Romi | 로미 |\n| Avery | 에이버리 | Jechan | 제찬 | Ron | 론 |\n| Avong | 아봉 | Jeff | 제프 | Roro | 로로 |\n| Azzi | 아찌 | Jenna | 제나 | Rowoon | 로운 |\n| Babilon | 바빌론 | Jennifer | 제니퍼 | Royce | 로이스 |\n| Bell | 벨 | Jeong Choi | 최정 | Rudolph | 루돌프 |\n| Ben | 벤 | Jeongah | 정아 | Ruri | 루리 |\n| Benny | 베니 | JeongJin | 정진 | Rusty | 러스티 |\n| Beri | 베리 | Jeongseob | 정섭 | Ryan | 라이언 |\n| Billie | 빌리 | Jerome | 제롬 | Ryueun | 류은 |\n| Bomi | 이보미 | Jewby | 쥬비 | Sabrina the Witch | 마녀 사브리나 |\n| Bonggyu | 봉규 | Jian | 지안 | Saeron | 새론 |\n| Bongman Kim | 김봉만 | Jicheol | 지철 | Salty Chan-gu | 삐뚤어진 찬구 |\n| Bono | 보노 | Jihee | 지희 | Samantha | 사만다 |\n| Bora | 보라 | Jihoon | 지훈 | Sammy | 새미 |\n| Brad | 브래드 | Jihyun | 지현 | Sangdo | 상도 |\n| Bruce | 브루스 | Jimmy | 지미 | Sanghoon | 상훈 |\n| Buddy | 버디 | Jin | 진 | Sangwoo | 상우 |\n| Bumsoo | 범수 | Jinhan | 진한 | Santa | 산타 |\n| Buttaguy | 빠다가이 | Jinhang Kim | 김진행 | Santa Claus | 산타클로스 |\n| Caitlyn | 케이틀린 | Jinhee | 진희 | Santa Reporter | VJ 싼타 |\n| Callan | 칼란 | Jinhyuk | 진혁 | Sara | 세라 |\n| Camila | 카밀라 | Jinseo | 진서 | Sean | 션 |\n| Captain Bill | 캡틴 빌 | Jinung | 진웅 | SeHee | 세희 |\n| Carl | 칼 | Jinwoo | 진우 | Seheon | 세헌 |\n| Carlos | 카를로스 | Jiwoo | 지우 | Sejin | 세진 |\n| Carol | 캐롤 | Jiyoon | 지윤 | Sena | 세나 |\n| Catherine | 캐서린 | Jiyoung | 지영 | Seohee | 서희 |\n| Chad | 채드 | Jodie | 조디 | Seojin | 서진 |\n| Chaeah | 채아 | Jolly | 졸리 | Seojoon | 서준 |\n| Chan-gu | 찬구 | Jongdae | 종대 | Seok Choi | 최석 |\n| Changbae | 창배 | Jooeun | 주은 | SeokPil | 한석필 |\n| Changhee | 창희 | Joonghyun | 중현 | Seokpyo | 석표 |\n| Changmin | 창민 | Joongsik | 중식 | Seolhwa | 설화 |\n| Changsu | 박창수 | Joonkyu | 준규 | Seonha | 선하 |\n| Chanhyuk | 찬혁 | Joshua | 조슈아 | Seoyeon | 서연 |\n| Charlotte | 샬롯 | Juha | 주하 | Seoyoon | 서윤 |\n| Chase | 체이스 | Juho | 주호 | Seungah | 승아 |\n| Cheolhoon | 철훈 | Jungbong | 정봉 | Seungheon | 승헌 |\n| Cheolyong | 리철용 | Junghee | 정희 | Seungho | 승호 |\n| Cherry | 체리 | Jungjae | 정재 | Seunghwa | 승화 |\n| Chester | 체스터 | Jungmin | 정민 | Seungjae | 승재 |\n| Chiho | 치호 | Jungseok | 정석 | Seungmoon | 승문 |\n| Chloe | 클로이 | Jungsoon | 정순 | Seungwon | 승원 |\n| Choyeon | 강초연 교관 | Jungwon | 정원 | Seungyeon | 승연 |\n| Chungah | 청아 | Junhee | 준희 | Sewoo | 세우 |\n| Chunsik Kang | 강춘식 기자 | Junho | 준호 | Shana | 샤나 |\n| Claire | 클레어 | JunKi | 준기 | Sheriff Kim | 김반장 |\n| Classic Narrator | 미스타 변사 | Junsang | 준상 | Shinhe | 신혜 |\n| Cole | 콜 | Junseong | 준성 | Shinwook | 신욱 |\n| Cox | 콕스 | Justice Roh | 정의로 | Shotgun | 샷건 |\n| Cyrus | 사이러스 | Justin | 저스틴 | Simon | 사이먼 |\n| Dabin | 다빈 | Juwon | 주원 | Sindarin | 신다린 |\n| Daegil | 대길 | Juyoung | 주영 | Sio | 시오 |\n| Dahee | 다희 | K-Santa | 산타 할아버지 | Siwon | 시원 |\n| Dahyeon | 다현 | Kangil | 강일 | Siwoo | 시우 |\n| Damian | 데이미언 | Kanno | 칸노 | Siyeon | 시연 |\n| Dan | 댄 | Katie | 케이티 | Skylar | 스카일러 |\n| Dana | 다나 | Kelly | 켈리 | Slushy | 슬러시 |\n| Dasom | 다솜 | Kelsey | 켈시 | Smoke | 스모크 |\n| David | 데이비드 | Kevin | 케빈 | Sohye | 소혜 |\n| Dean | 딘 | Keybo | 키보 | Sojin | 소진 |\n| Deokhwan | 덕환 | Kijang Kim | 김기장 | Soobin | 수빈 |\n| Dohan | 도한 | Killian the Vampire | 뱀파이어 킬리언 | Sookhee | 숙희 |\n| Dohee | 도희 | Kiseob | 기섭 | Sooni | 순이 |\n| Dohyun | 도현 | Klip Kim | 클립 킴 | Sophia | 소피아 |\n| Dollar Jr. | 달러 주니어 | Koombo | 쿰보 | Sora | 소라 |\n| Doug | 더그 | Kristen | 크리스틴 | Soye | 소예 |\n| Doughnut | 도넛 | Kukhee | 국희 | Soyi | 소이 |\n| DU5T | 더스트 | Kwonil | 권일 | Soyoung | 소영 |\n| Duckchun | 덕춘 | Kyumin | 규민 | Soyul | 소율 |\n| Duke | 듀크 | Kyungho | 경호 | Spice | 스파이스 |\n| Dukgu | 덕구 | Kyungsoo | 경수 | Sportscaster Kang | 강수정 캐스터 |\n| Duman | 두만 | Kyungsook | 경숙 | Sportscaster Tony | 이영광 캐스터 |\n| Dupil | 곽두필 | Lady Cho | 단희빈 | Starling | 스탈링 |\n| DVZY | 데이지 | Lala | 라라 | Stephanie | 스테파니 |\n| Dylan | 딜런 | Lamie | 라미 | Storyteller Jinsub | 송진섭 스토리텔러 |\n| E-seller Sherri | 쇼린이 | Landon | 랜던 | Sua | 수아 |\n| Echo | 에코 | Larry | 래리 | Suho | 수호 |\n| Edward | 에드워드 | Leo | 레오 | Suji | 수지 |\n| Elias | 엘리아스 | Liam | 리암 | Sujin | 수진 |\n| Elise | 엘리스 | Lindsay | 린지 | Sullock Hong | 홍설록 |\n| Ella | 엘라 | Liz | 리즈 | Sumin | 수민 |\n| Eman | 이만 | Lloyd | 로이드 | Sungbae | 성배 |\n| Emma | 엠마 | Logan | 로건 | Sunggyu | 성규 |\n| Eunbin | 은빈 | Lucille | 루실 | Sungho | 성호 |\n| Eunchae | 은채 | Luna | 루나 | Sunghoon | 성훈 |\n| Eunha | 은하 | Lydia | 리디아 | Sunghyun | 성현 |\n| Eunsol | 은솔 | Maddie | 매디 | Sungjun | 홈쇼핑 성준 |\n| Frankenstein | 프랑켄슈타인 | Maisie | 메이시 | Sungkwon | 성권 |\n| Furnando | 터르난도 | Margaret | 마가렛 | Sungtae | 성태 |\n| Gaeul | 가을 | Margot | 마고 | Sungwook | 성욱 |\n| Gahee | 가희 | Matthew | 매튜 | Sunyoung | 선영 |\n| George | 조지 | MBTI EF (F) | MBTI EF 여 | Suyoon | 수윤 |\n| GeumHee | 금희 | MBTI EF (M) | MBTI EF 남 | Sylvia | 실비아 |\n| Geunhyeok | 근혁 | MBTI ET (F) | MBTI ET 여 | Taebaek | 장태백 |\n| Geunseok | 근석 | MBTI ET (M) | MBTI ET 남 | Taeji | 태지 |\n| Geunwoo | 근우 | MBTI IF (F) | MBTI IF 여 | Taejoong | 태중 |\n| Geunyeong | 근영 | MBTI IF (M) | MBTI IF 남 | Taemin | 태민 |\n| Ggami | 까미 | MBTI IT (F) | MBTI IT 여 | Taesub | 태섭 |\n| Ggomi | 꼬미 | MBTI IT (M) | MBTI IT 남 | Taewoo | 태우 |\n| Glenda | 글렌다 | MC Kong | MC콩 | Teal | 틸 |\n| Goat Kim | 김고트 | MC TypeCast | MC 타캐 | Tessa | 테사 |\n| Gongchul | 공철 | Mia | 미아 | Tian | 티안 |\n| Gowoon | 고운 | Michael | 마이클 | Tim | 팀 |\n| Grace | 그레이스 | Mija | 오미자 | Tina | 티나 |\n| Graham | 그레이엄 | Mijin | 미진 | Toby | 토비 |\n| Gunseok | 건석 | Mikyung | 미경 | Tommy | 토미 |\n| Gunwoo | 건우 | Millie | 밀리 | Tyson | 타이슨 |\n| Guri | 구리 | Minchae | 민채 | UiChan | 의찬이 |\n| Gus | 거스 | Minji | 민지 | Uncle Hank | 행크 |\n| Ha Eun | 하은 | Minjoon | 민준 | Valerie | 밸러리 |\n| Haejun | 해준 | Minju | 민주 | Valkyrie | 발키리 |\n| Haerang | 해랑 | Minjung | 민정 | Vanessa | 바네사 |\n| Hailey | 헤일리 | Minsang | 민상 | Verna | 베르나 |\n| Hajun | 하준 | Minsu | 민수 | Victoria | 빅토리아 |\n| Hakchul | 학철 | Mio | 미오 | Viktor | 빅토르 |\n| Hamchu | 햄쮸 | Miran Choi | 최미란 | Vincent | 빈센트 |\n| Han Taesung Caster | 한태성 캐스터 | Mirine | 미리내 | Vivien | 비비안 |\n| Hana | 하나 | Miseon | 미선 | Wade | 웨이드 |\n| Hangyeol | 한결 | Miso | 미소 | Walter | 월터 |\n| Hanjun | 한준 | Mister Gop | 미스터 갑 | Wangkwon | 왕권 |\n| Hanna | 한나 | Monggun | 몽군 | Weather Reporter Sky | 하늘 캐스터 |\n| Hans | 한스 | Moonjung | 문정 | West | 웨스트 |\n| Hansol | 한솔 | Mooyeol | 무열 | Wonho | 원호 |\n| Hanyoung | 한영 | Morgan | 모건 | Wonkyung | 원경 |\n| Harin | 하린 | Moru | 머루 | Wonwoo | 원우 |\n| Harper | 하퍼 | Mrs. Claus | 미세스 클로스 | Wooju | 우주 |\n| Hayul | 하율 | Munseok | 문석 | Woosung | 우성 |\n| Heejoon | 희준 | Munsu | 문수 | Xavier | 자비에르 |\n| Helena | 헬레나 | Myeonghee | 명희 | Yeeun | 예은 |\n| Henry | 헨리 | Myungil | 명일 | Yejin | 예진 |\n| Homun Sim | 심호문 | Myungjoo | 명주 | Yejoon | 예준 |\n| Hoon | 훈 | Naeun | 나은 | YeLin | 예린 |\n| Hosik | 호식 | Najin | 나진 | Yena | 예나 |\n| Hosun | 호선 | Namjoon | 남준 | Yeonah | 연아 |\n| Hoyoung | 호영 | Nana | 나나 | Yeonggeol | 영걸 |\n| Hugh | 휴 | Nari | 개나리 | Yeonhwa | 연화 |\n| Hwimin | 휘민 | Nathan | 네이선 | Yeonja | 연자 |\n| Hyejung | 혜정 | Neel | 닐 | Yeonsuh | 연서 |\n| Hyelee | 혜리 | Neoguard | 네오가드 | Yeonwoo | 연우 |\n| Hyemin | 혜민 | News anchor Jinsub | 송진섭 앵커 | Yeseul | 예슬 |\n| Hyena | 혜나 | Newscaster John | 존 앵커 | Yongsik | 용식이 |\n| Hyeongjin | 형진 | Nia | 니아 | Yoonseo | 윤서 |\n| Hyera | 혜라 | Noa | 노아 | Yoonsung | 윤성 |\n| Hyesu | 홈쇼핑 혜수 | Noel | 노엘 | Younggil | 영길 |\n| Hyewon | 혜원 | Noeul | 노을 | Younghee | 영희 |\n| Hyun | 현 | Norah | 노라 | Younghwan | 영환 |\n| Hyunji | 현지 | Nova | 노바 | Youngji | 영지 |\n| Hyunjin | 현진 | Old radio | 레디오 | Youngkyu | 영규 |\n| Hyunju | 현주 | Oliver | 올리버 | Youngmok | 영목 |\n| Hyunkyung | 현경 | Olivia | 올리비아 | Yubin | 유빈 |\n| Hyunmin | 현민 | Oscar | 오스카 | Yujin | 유진 |\n| Hyunseung | 현승 | Owen | 오언 | Yumi | 유미 |\n| HyunWoo | 현우 | P-0150N | 포이즌 | Yumin | 유민 |\n| Ian | 이안 | Pacang | 파캉 | Yunbin | 윤빈 |\n| Icarus | 이카루스 | Paige | 페이지 | Yunjeong | 윤정 |\n| Igyeom | 이겸 | Pangpang | 팡팡 | Yura | 유라 |\n| Ijun | 이준 | Patricia | 패트리샤 | Yuri | 유리 |\n| Ilho | 일호 | Patrick | 패트릭 | Yuseong | 유성 |\n| Ina | 이나 | Peter | 피터 | Zoey | 조이 |\n| Inhwa | 인화 | Philip | 필립 | | |\n\n
\n" }, "voice_name": { "type": "string", "title": "Voice Name", "description": "사람이 읽을 수 있는 보이스 이름" }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입 — `original` 은 타입캐스트가 기본 제공하는 스톡 보이스, `custom` 은 사용자가 업로드/클론한 보이스입니다." } }, "description": "모델별로 그룹화된 감정과 향상된 메타데이터가 있는 V2 보이스 응답 모델" }, "VoiceV3": { "type": "object", "title": "VoiceV3", "required": [ "voice_id", "voice_name", "voice_type", "models" ], "properties": { "age": { "anyOf": [ { "$ref": "#/components/schemas/AgeEnum" }, { "type": "null" } ], "description": "보이스 연령대입니다." }, "gender": { "anyOf": [ { "$ref": "#/components/schemas/GenderEnum" }, { "type": "null" } ], "description": "보이스 성별(`male` 또는 `female`)입니다." }, "models": { "type": "array", "items": { "$ref": "#/components/schemas/ModelInfo" }, "title": "Models", "description": "지원하는 TTS 모델과 감정 목록입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "고유 보이스 식별자입니다. 기본 보이스는 `tc_`, 커스텀 보이스는 `uc_`로 시작합니다." }, "use_cases": { "type": "array", "items": { "type": "string" }, "title": "Use Cases", "description": "추천 사용 사례 목록입니다." }, "voice_name": { "$ref": "#/components/schemas/VoiceName", "description": "`{\"eng\": \"Daejin\", \"kor\": \"대진\"}` 같은 다국어 보이스 이름입니다." }, "voice_type": { "$ref": "#/components/schemas/VoiceType", "description": "보이스 타입(`original` 또는 `custom`)입니다." }, "preview_url": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Preview Url", "description": "제공되는 경우 사용할 수 있는 미리듣기 오디오 URL입니다." } }, "description": "다국어 이름과 미리듣기 URL을 포함한 보이스 정보입니다." }, "PlanTier": { "enum": [ "free", "lite", "plus", "custom" ], "type": "string", "title": "PlanTier", "description": "API 플랜.\n\n사용 가능한 값:\n- **free**: 제한된 크레딧의 무료 등급\n- **lite**: 중간 수준 크레딧의 라이트 플랜\n- **plus**: 높은 크레딧의 플러스 플랜\n- **custom**: 커스텀 엔터프라이즈 플랜" }, "TTSModel": { "enum": [ "ssfm-v30", "ssfm-v21" ], "type": "string", "title": "TTSModel", "description": "음성 합성에 사용할 TTS 모델 버전. 다양한 모델은 다양한 기능과 품질 수준을 제공합니다.\n\n사용 가능한 모델:\n- **ssfm-v30**: 향상된 플로우와 추가 감정 프리셋이 있는 최신 모델(권장)\n- **ssfm-v21**: 검증된 신뢰성과 일관된 품질을 갖춘 안정적인 모델\n" }, "ModelInfo": { "type": "object", "title": "ModelInfo", "required": [ "version", "emotions" ], "properties": { "version": { "$ref": "#/components/schemas/TTSModel", "description": "TTS 모델 버전(예: ssfm-v21, ssfm-v30)" }, "emotions": { "type": "array", "items": { "type": "string" }, "title": "Emotions", "description": "이 모델에서 지원되는 감정 목록" } }, "description": "버전 및 지원되는 감정을 포함한 모델 정보" }, "VoiceName": { "type": "object", "title": "VoiceName", "required": [ "eng", "kor" ], "properties": { "eng": { "type": "string", "title": "Eng", "description": "영어 보이스 이름입니다." }, "kor": { "type": "string", "title": "Kor", "description": "한국어 보이스 이름입니다." } }, "description": "ISO 639-3 언어 코드를 키로 사용하는 다국어 보이스 이름입니다." }, "VoiceType": { "enum": [ "original", "custom" ], "type": "string", "title": "VoiceType", "description": "보이스 타입 분류.\n\n- `original` — 타입캐스트가 기본 제공하는 스톡 보이스로, 모든 계정에서 사용 가능합니다.\n- `custom` — 사용자가 본인의 샘플을 업로드하거나 클로닝해서 만든 보이스입니다." }, "GenderEnum": { "enum": [ "male", "female" ], "type": "string", "title": "GenderEnum", "description": "성별 분류 열거형 - 데이터베이스 값(한국어)을 API 값(영어)으로 변환합니다.\n\n사용 가능한 값:\n- **male**: 남성 보이스\n- **female**: 여성 보이스\n" }, "EmotionEnum": { "enum": [ "normal", "sad", "happy", "angry", "whisper", "toneup", "tonedown" ], "type": "string", "title": "EmotionEnum", "description": "음성 합성에 사용 가능한 감정 프리셋. 각 감정은 생성된 음성의 톤, 속도, 표현력에 영향을 줍니다.\n\n**ssfm-v21 지원 감정 (4종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n\n**ssfm-v30 지원 감정 (7종류):**\n- normal: 중립적이고 균형 잡힌 톤\n- happy: 밝고 명랑한 표현\n- sad: 우울하고 차분한 톤\n- angry: 강하고 강렬한 전달\n- whisper: 부드럽고 조용한 말\n- toneup: 더 높은 톤 강조\n- tonedown: 더 낮은 톤 강조\n\n/v2/voices API 응답을 통해 각 음성에 사용 가능한 감정을 확인하세요.\n" }, "SmartPrompt": { "type": "object", "title": "스마트 프롬프트 (ssfm-v30)", "example": { "next_text": "I am literally bursting with happiness and I never want this feeling to end!", "emotion_type": "smart", "previous_text": "I feel like I'm walking on air and I just want to scream with joy!" }, "properties": { "next_text": { "type": "string", "title": "Next Text", "default": "", "example": "I am literally bursting with happiness and I never want this feeling to end!", "description": "TTSRequest의 `text` 필드 이후에 오는 텍스트. 감정 추론을 위한 전방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 전환을 예측하는 데 도움\r\n- 다음 컨텍스트가 없으면 비워 둡니다\r\n" }, "emotion_type": { "type": "string", "const": "smart", "title": "Emotion Type", "default": "smart", "description": "프롬프트 유형을 식별하는 판별자 필드. 컨텍스트 인식 감정 추론을 위해 \"smart\"로 설정해야 합니다.\r\n" }, "previous_text": { "type": "string", "title": "Previous Text", "default": "", "example": "I feel like I'm walking on air and I just want to scream with joy!", "description": "TTSRequest의 `text` 필드 이전에 오는 텍스트. 감정 추론을 위한 후방 컨텍스트를 제공합니다.\r\n\r\n모델은 흐름을 분석합니다: `previous_text` → `text`(합성됨) → `next_text`\r\n\r\n- 최대 2000자\r\n- 모델이 감정 빌드업과 컨텍스트를 이해하는 데 도움\r\n- 이전 컨텍스트가 없으면 비워 둡니다\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "PresetPrompt": { "type": "object", "title": "프리셋 프롬프트 (ssfm-v30)", "properties": { "emotion_type": { "type": "string", "const": "preset", "title": "Emotion Type", "default": "preset", "description": "프롬프트 유형을 식별하는 판별자 필드. 프리셋 기반 감정 제어를 위해 \"preset\"으로 설정해야 합니다.\r\n" }, "emotion_preset": { "$ref": "#/components/schemas/EmotionEnum", "default": "normal", "example": "normal", "description": "생성된 음성에 적용할 감정 프리셋.\r\n\r\n지원되는 감정: normal, happy, sad, angry, whisper, toneup, tonedown\r\n\r\n/v2/voices API를 통해 각 보이스에 사용 가능한 감정을 확인하세요.\r\n" }, "emotion_intensity": { "type": "number", "title": "Emotion Intensity", "default": 1, "example": 1, "maximum": 2, "minimum": 0, "description": "생성된 음성의 감정 표현 강도를 제어합니다.\r\n\r\n- 0.0: 완전히 중립적, 감정 색채 없음\r\n- 0.5: 미묘한 감정 힌트\r\n- 1.0: 표준 감정 표현(기본값)\r\n- 1.5: 강한 감정 강조\r\n- 2.0: 최대 강도, 매우 표현력 있음\r\n" } }, "description": "생성된 음성의 감정 및 스타일 설정.", "additionalProperties": false }, "UseCasesEnum": { "enum": [ "Announcer", "Anime", "Audiobook", "Conversational", "Documentary", "E-learning", "Rapper", "Game", "Tiktok/Reels", "News", "Podcast", "Voicemail", "Ads" ], "type": "string", "description": "콘텐츠 유형 필터링을 위한 보이스 사용 사례 카테고리. 각 보이스는 특정 콘텐츠 유형에 대한 적합성을 나타내는 하나 이상의 사용 사례로 태그됩니다.\n\n**사용 가능한 사용 사례:**\n- **Announcer**: 공공 발표 및 프레젠테이션\n- **Anime**: 애니메이션 보이스\n- **Audiobook**: 장문 내레이션 및 스토리텔링\n- **Conversational**: 챗봇 및 대화형 AI\n- **Documentary**: 다큐멘터리 내레이션 및 해설\n- **E-learning**: 교육 콘텐츠 및 튜토리얼\n- **Rapper**: 랩 및 음악 퍼포먼스\n- **Game**: 비디오 게임 보이스 및 내레이션\n- **Tiktok/Reels**: SNS 숏츠\n- **News**: 뉴스 방송\n- **Podcast**: 방송 및 팟캐스트 제작\n- **Voicemail**: IVR 시스템 및 음성 비서\n- **Ads**: 광고 및 홍보 콘텐츠\n" }, "ErrorResponse": { "type": "object", "example": { "message": "The input text contains characters or symbols that cannot be synthesized into speech. Please check your input text.", "error_code": "TEXT_NOT_SYNTHESIZABLE" }, "properties": { "detail": { "type": "string", "description": "문제를 설명하는 오류 메시지" }, "message": { "type": "string", "description": "구조화된 오류에 대한 설명" }, "error_code": { "type": "string", "description": "구조화된 오류를 식별하는 코드" } }, "description": "기존 오류와 유효성 검사 오류는 `detail`을 사용하며, 구조화된 오류는 `error_code`와 `message`를 사용합니다." }, "CustomVoiceItem": { "type": "object", "title": "CustomVoiceItem", "required": [ "voice_id", "name", "model", "source", "status", "created_at" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "title": "Error", "description": "`status`가 `failed`일 때의 실패 사유입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "source": { "type": "string", "title": "Source", "description": "생성 방식(`instant` 또는 `professional`)입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." }, "created_at": { "type": "string", "title": "Created At", "format": "date-time", "description": "UTC 기준 생성 시각입니다." } }, "description": "인증된 계정이 소유한 커스텀 보이스입니다." }, "ValidationError": { "type": "object", "title": "ValidationError", "required": [ "loc", "msg", "type" ], "properties": { "ctx": { "type": "object", "title": "Context" }, "loc": { "type": "array", "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" } } }, "RecommendedVoice": { "type": "object", "title": "RecommendedVoice", "required": [ "voice_id", "voice_name", "score" ], "properties": { "score": { "type": "number", "title": "Score", "example": 0.92, "description": "추천 관련도 점수. 값이 높을수록 검색어와 더 잘 맞는 후보입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "example": "tc_60e5426de8b95f1d3000d7b5", "description": "`tc_` prefix 가 붙은 타입캐스트 보이스 식별자. 텍스트 음성 변환 요청의 `voice_id` 로 사용할 수 있습니다." }, "voice_name": { "type": "string", "title": "Voice Name", "example": "Olivia", "description": "사람이 읽을 수 있는 보이스 이름." } }, "description": "`GET /v1/voices/recommendations` 응답의 보이스 추천 후보. 관련도 순서로 정렬됩니다." }, "CustomVoiceStatus": { "enum": [ "pending", "training", "completed", "failed" ], "type": "string", "title": "CustomVoiceStatus", "description": "커스텀 보이스의 현재 생성 또는 학습 상태입니다." }, "CustomVoiceResponse": { "type": "object", "title": "CustomVoiceResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름 (1~30자)." }, "model": { "type": "string", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "title": "Model", "description": "보이스가 클로닝된 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "생성/학습 상태" }, "voice_id": { "type": "string", "title": "Voice Id", "example": "uc_64a1b2c3d4e5f6a7b8c9d0e1", "description": "`uc_` prefix 가 붙은 커스텀 보이스 식별자. `POST /v1/text-to-speech` 등 `voice_id` 를 받는 엔드포인트에 그대로 사용할 수 있습니다." } }, "description": "`POST /v1/voices/clone` 응답 — 퀵 클로닝으로 생성된 커스텀 보이스 메타데이터." }, "HTTPValidationError": { "type": "object", "title": "HTTPValidationError", "properties": { "detail": { "type": "array", "items": { "$ref": "#/components/schemas/ValidationError" }, "title": "Detail" } } }, "PauseComposeSegment": { "type": "object", "title": "PauseComposeSegment", "examples": [ { "type": "pause", "duration_seconds": 1.5 } ], "required": [ "type", "duration_seconds" ], "properties": { "type": { "type": "string", "const": "pause", "title": "Type", "default": "pause", "description": "세그먼트 구분값입니다. 항상 `pause`입니다." }, "duration_seconds": { "type": "number", "title": "Duration Seconds", "example": 1.5, "maximum": 10, "description": "초 단위 쉼 길이입니다. 개별 쉼은 최대 10초, 모든 쉼의 합은 최대 60초입니다.", "exclusiveMinimum": 0 } }, "description": "크레딧 차감 없이 삽입되는 무음 구간입니다.", "additionalProperties": false }, "AlignmentSegmentWord": { "type": "object", "title": "AlignmentSegmentWord", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 단어 단위 정렬 구간." }, "SubscriptionResponse": { "type": "object", "title": "SubscriptionResponse", "required": [ "plan", "credits", "limits", "professional_clone_generations" ], "properties": { "plan": { "$ref": "#/components/schemas/PlanTier", "description": "현재 구독 플랜명" }, "limits": { "$ref": "#/components/schemas/Limits", "description": "사용 제한 정보" }, "credits": { "$ref": "#/components/schemas/Credits", "description": "크레딧 사용 정보" }, "professional_clone_generations": { "$ref": "#/components/schemas/ProfessionalCloneGenerations" } }, "description": "구독 정보 응답 모델" }, "AlignmentSegmentCharacter": { "type": "object", "title": "AlignmentSegmentCharacter", "required": [ "text", "start", "end" ], "properties": { "end": { "type": "number", "title": "End", "description": "이 구간의 종료 시각(오디오 시작 기준 초)." }, "text": { "type": "string", "title": "Text", "description": "원본 transcript 의 텍스트 조각 (문장부호/공백 포함)." }, "start": { "type": "number", "title": "Start", "description": "이 구간의 시작 시각(오디오 시작 기준 초)." } }, "description": "원본 transcript 와 생성된 오디오 사이의 문자 단위 정렬 구간." }, "CustomVoiceCreateResponse": { "type": "object", "title": "CustomVoiceCreateResponse", "required": [ "voice_id", "name", "model", "status" ], "properties": { "name": { "type": "string", "title": "Name", "description": "보이스 이름입니다." }, "model": { "type": "string", "title": "Model", "description": "TTS 모델 버전입니다." }, "status": { "$ref": "#/components/schemas/CustomVoiceStatus", "description": "현재 생성 또는 학습 상태입니다." }, "voice_id": { "type": "string", "title": "Voice Id", "description": "`uc_`로 시작하는 커스텀 보이스 고유 식별자입니다." } }, "description": "퀵 클로닝 또는 프리미엄 클로닝 생성 요청 결과입니다." }, "TTSWithTimestampsResponse": { "type": "object", "title": "TTSWithTimestampsResponse", "required": [ "audio", "audio_format", "audio_duration", "words", "characters" ], "properties": { "audio": { "type": "string", "title": "Audio", "description": "base64 로 인코딩된 오디오 바이트. `audio_format` 확장자로 디코딩해 파일로 저장할 수 있습니다." }, "words": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentWord" } }, { "type": "null" } ], "title": "Words", "description": "단어 단위 타임스탬프(문장부호 포함). 요청이 `granularity=char` 일 때는 `null`." }, "characters": { "anyOf": [ { "type": "array", "items": { "$ref": "#/components/schemas/AlignmentSegmentCharacter" } }, { "type": "null" } ], "title": "Characters", "description": "문자 단위 타임스탬프(문장부호와 공백 포함). 요청이 `granularity=word` 일 때는 `null`." }, "audio_format": { "enum": [ "wav", "mp3" ], "type": "string", "title": "Audio Format", "description": "`audio` 필드의 오디오 인코딩 포맷 — `wav` 또는 `mp3` (요청의 `output.audio_format` 에 따라 결정)." }, "audio_duration": { "type": "number", "title": "Audio Duration", "description": "생성된 오디오의 길이(초)." } }, "description": "TTS 생성 + 타임스탬프 정렬 통합 응답." }, "ProfessionalCloneGenerations": { "type": "object", "title": "ProfessionalCloneGenerations", "properties": { "plan_generations": { "type": "integer", "title": "Plan Generations", "default": 0, "minimum": 0, "description": "플랜 제공 학습 횟수" }, "used_generations": { "type": "integer", "title": "Used Generations", "default": 0, "minimum": 0, "description": "사용된 학습 횟수" } }, "description": "프리미엄 클로닝 학습 횟수 정보" }, "Body_create_voice_clone_v1_voices_clone_post": { "type": "object", "title": "Body_create_voice_clone_v1_voices_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "File", "format": "binary", "description": "오디오 샘플. WAV 또는 MP3, 최대 25MB, 5초 이상 150초 이하.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "maxLength": 30, "minLength": 1, "description": "보이스 이름 (1~30자)." }, "model": { "$ref": "#/components/schemas/TTSModel", "allOf": [ { "$ref": "#/components/schemas/TTSModel" } ], "description": "클로닝할 엔진 모델 (`ssfm-v21` 또는 `ssfm-v30`)." } }, "description": "퀵 클로닝 요청을 위한 multipart 본문." }, "Body_create_instant_clone_v1_custom_voices_instant_clone_post": { "type": "object", "title": "Body_create_instant_clone_v1_custom_voices_instant_clone_post", "required": [ "file", "name", "model" ], "properties": { "file": { "type": "string", "title": "파일", "format": "binary", "description": "25 MiB 이하이면서 길이가 5초 이상 150초 이하인 WAV 또는 MP3 음성 파일 1개입니다.", "contentMediaType": "application/octet-stream" }, "name": { "type": "string", "title": "Name", "example": "상품 내레이터", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." } }, "description": "퀵 클로닝용 multipart 요청 본문입니다." }, "Body_create_professional_clone_v1_custom_voices_professional_clone_post": { "type": "object", "title": "Body_create_professional_clone_v1_custom_voices_professional_clone_post", "required": [ "name", "files", "model", "language" ], "properties": { "name": { "type": "string", "title": "Name", "example": "커스텀 보이스 이름", "maxLength": 30, "minLength": 1, "description": "최대 30자의 보이스 이름입니다." }, "files": { "type": "array", "items": { "type": "string", "format": "binary", "contentMediaType": "application/octet-stream" }, "title": "파일", "description": "학습에 사용할 WAV 또는 MP3 음성 파일입니다. 하나의 파일을 업로드할 수 있습니다.\r\n\r\n**업로드 제한**\r\n\r\n* WAV 또는 MP3 파일 1개\r\n* 파일 크기: 1 GiB 이하\r\n* 길이: 5분 이상 3시간 이하\r\n* 샘플레이트: 16 kHz 이상\r\n\r\n최상의 결과를 위해 다음 조건을 충족하는 오디오를 권장합니다.\r\n\r\n* 생성된 보이스가 재현하기를 원하는 말투와 최대한 가깝게 녹음해 주세요.\r\n* 배경 소음이 없는 조용한 환경에서 녹음해 주세요.\r\n* 한 명의 화자 목소리만 포함해 주세요.\r\n* `language` 필드에 지정한 언어로 녹음해 주세요.\r\n* 오디오 길이가 길수록 생성되는 보이스의 품질이 향상됩니다." }, "model": { "$ref": "#/components/schemas/TTSModel", "example": "ssfm-v30", "description": "TTS 모델 버전입니다." }, "language": { "type": "string", "title": "Language", "example": "kor", "description": "`kor`, `eng` 같은 ISO 639-3 언어 코드입니다." } }, "description": "프리미엄 클로닝용 multipart 요청 본문입니다." } }, "securitySchemes": { "ApiKeyAuth": { "in": "header", "name": "X-API-KEY", "type": "apiKey", "description": "인증을 위한 API 키. 타입캐스트 API 콘솔에서 API 키를 생성할 수 있습니다." }, "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" } } } } ```