> ## 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

<CardGroup cols={2}>
<Card title="Package" icon="python" href="https://pypi.org/project/typecast-python/">
Typecast Python SDK
</Card>

<Card title="Source Code" icon="github" href="https://github.com/neosapience/typecast-sdk/tree/main/typecast-python">
Typecast Python SDK Source Code
</Card>
</CardGroup>

<Note>
Supports **Python 3.11–3.14**. Upgrade older Python environments before installing the latest SDK.
</Note>

<Accordion title="Compatibility and migration for older Python versions">
**Python 3.8, 3.9, and 3.10 are no longer supported because they have reached end of life (EOL).** The last compatible SDK is **typecast-python 0.3.15** for Python 3.8/3.9 and **0.4.1** for Python 3.10. Upgrade Python to 3.11 or later (below 3.15), then install the current SDK. If you must temporarily keep Python 3.10, pin `python -m pip install "typecast-python==0.4.1"`; for Python 3.8/3.9, use `typecast-python==0.3.15`. An older package pin does not restore security support for an EOL runtime.
</Accordion>

## Installation

Install the Typecast Python SDK using pip:

```bash
pip install --upgrade typecast-python
```

<Note>The package is installed as `typecast-python`, but imported as `typecast`.</Note>

<Warning>
Latest registered version: **0.5.0** on PyPI. Make sure you have **version 0.5.0 or higher** installed on a supported Python runtime. You can check your version with `pip show typecast-python`. If you have an older version, run `pip install --upgrade typecast-python` to update.
</Warning>

## 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:

<CodeGroup>

```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")
```

</CodeGroup>

<Info>
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.
</Info>

```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**.

<Tabs>
  <Tab title="Smart Mode">
    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
        )
    ))
    ```
  </Tab>

  <Tab title="Preset Mode">
    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
        )
    ))
    ```
  </Tab>
</Tabs>

### 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
    ),
))
```

### 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, or prompt. 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]
```

<Note>
**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.
</Note>

## 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")
```

<Note>
**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.
</Note>

## 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)
```

<Warning>
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**.
</Warning>

## 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)
```
