> ## 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.neosapiencetypecast-java1.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