Here's the complete breakdown for the dev team.

---

## Summary: 5 API Classes across 4 distinct APIs

| # | `api_class` | API (SDK Module) | Models Using It | SDK Method |
|---|---|---|---|---|
| 1 | `elevenlabs_tts` | `client.text_to_speech` | `eleven_flash_v2_5` (latency-critical), `eleven_v3` (quality) | `.convert()` / `.stream()` |
| 2 | `elevenlabs_dialogue` | `client.text_to_dialogue` | `eleven_v3` | `.convert()` / `.stream()` |
| 3 | `elevenlabs_stt` | `client.speech_to_text` | `scribe_v2` | `.convert()` |
| 4 | `elevenlabs_stt_realtime` | `client.speech_to_text.realtime` | `scribe_v2_realtime` | `.connect()` |
| 5 | `elevenlabs_music` | `client.music` | `music_v1` | `.compose()` / `.stream()` |

---

## API 1: Text to Speech (`client.text_to_speech`)

**One api_class: `elevenlabs_tts`** — used by `eleven_flash_v2_5` (latency-critical: realtime/chat) and `eleven_v3` (quality, the default for stored audio). `eleven_multilingual_v2` is DEPRECATED — never use it.

These models are interchangeable on the exact same method. The `model_id` parameter selects which one. Same signature, same return type.

```python
from elevenlabs import ElevenLabs, VoiceSettings

client = ElevenLabs(api_key="...")

# Non-streaming (returns Iterator[bytes])
audio = client.text_to_speech.convert(
    voice_id="JBFqnCBsd6RMkjVDRZzb",         # str, required (path param)
    text="Hello world",                         # str, required
    model_id="eleven_v3",                       # str, optional (default: eleven_v3; use eleven_flash_v2_5 for realtime/chat)
    output_format="mp3_44100_128",              # enum, optional (default: mp3_44100_128)
    language_code="en",                         # str|None, optional
    voice_settings=VoiceSettings(               # VoiceSettings|None, optional
        stability=0.5,
        similarity_boost=0.75,
        style=0.0,
        speed=1.0,
        use_speaker_boost=True,
    ),
    seed=42,                                    # int|None, optional (0–4294967295)
    previous_text="...",                        # str|None, optional
    next_text="...",                            # str|None, optional
    previous_request_ids=["req_abc"],           # list[str]|None, optional (max 3)
    next_request_ids=["req_def"],               # list[str]|None, optional (max 3)
    apply_text_normalization="auto",            # enum: "auto"|"on"|"off", optional
    apply_language_text_normalization=False,     # bool, optional (Japanese only, high latency)
    pronunciation_dictionary_locators=[...],    # list|None, optional (max 3)
    enable_logging=True,                        # bool, optional (query param)
)

# Streaming (returns Iterator[bytes])
audio_stream = client.text_to_speech.stream(
    # Exact same parameters as convert()
)
```

Also available: `.convert_with_timestamps()` and `.stream_with_timestamps()` — same params, but return includes word-level timing alignment data.

---

## API 2: Text to Dialogue (`client.text_to_dialogue`)

**One api_class: `elevenlabs_dialogue`** — used by `eleven_v3` only

This is a structurally different API. Instead of a single `voice_id` + `text`, it takes a list of `DialogueInput` objects (each with their own `text` and `voice_id`). No `voice_settings` per-voice. No `previous_text`/`next_text` stitching.

```python
from elevenlabs import ElevenLabs, DialogueInput

client = ElevenLabs(api_key="...")

audio = client.text_to_dialogue.convert(
    inputs=[                                    # list[DialogueInput], required
        DialogueInput(
            text="Knock knock",
            voice_id="JBFqnCBsd6RMkjVDRZzb",
        ),
        DialogueInput(
            text="Who is there?",
            voice_id="Aw4FAjKCGjjNkVhN1Xmq",
        ),
    ],
    model_id="eleven_v3",                       # str, optional (default: eleven_v3)
    output_format="mp3_44100_128",              # enum, optional
    language_code="en",                         # str|None, optional
    settings=None,                              # ModelSettingsResponseModel|None, optional
    pronunciation_dictionary_locators=[...],    # list|None, optional
    seed=42,                                    # int|None, optional
    apply_text_normalization="auto",            # enum: "auto"|"on"|"off", optional
)
```

Also available: `.stream()`, `.convert_with_timestamps()`, `.stream_with_timestamps()`

**Key difference from TTS:** Max 10 unique voice IDs per request. No `voice_settings`, no `previous_text`/`next_text`, no `previous_request_ids`/`next_request_ids`.

---

## API 3: Speech to Text — Batch (`client.speech_to_text`)

**One api_class: `elevenlabs_stt`** — used by `scribe_v2`

Completely different API pattern. Takes audio file in, returns structured JSON transcript out.

```python
from elevenlabs import ElevenLabs

client = ElevenLabs(api_key="...")

# Returns SpeechToTextConvertResponse (not bytes)
result = client.speech_to_text.convert(
    model_id="scribe_v2",                       # enum: "scribe_v2"|"scribe_v1", required
    file=open("audio.mp3", "rb"),               # file, optional (one of file or source_url required)
    source_url="https://...",                   # str|None, optional (YouTube, TikTok, etc.)
    language_code="en",                         # str|None, optional (auto-detected if omitted)
    tag_audio_events=True,                      # bool, optional (default: true)
    num_speakers=2,                             # int|None, optional (1–32)
    timestamps_granularity="word",              # enum: "none"|"word"|"character", optional
    diarize=False,                              # bool, optional (default: false)
    diarization_threshold=0.22,                 # float|None, optional (0.1–0.4)
    additional_formats=[...],                   # list, optional (SRT, VTT, etc.)
    file_format="other",                        # enum: "pcm_s16le_16"|"other", optional
    temperature=0.0,                            # float|None, optional (0–2)
    seed=42,                                    # int|None, optional (0–2147483647)
    use_multi_channel=False,                    # bool, optional (max 5 channels)
    entity_detection="all",                     # str|list|None, optional (+30% cost)
    entity_redaction="pii",                     # str|list|None, optional (+30% cost)
    entity_redaction_mode="enumerated_entity_type",  # str, optional
    no_verbatim=False,                          # bool, optional (scribe_v2 only)
    keyterms=["ElevenLabs", "TTS"],             # list[str]|None, optional (max 1000, +20% cost)
    webhook=False,                              # bool, optional
    webhook_id="...",                           # str|None, optional
    webhook_metadata={...},                     # str|dict|None, optional
    enable_logging=True,                        # bool, optional
)

# Access results
print(result.text)           # Full transcript text
print(result.language_code)  # Detected language
print(result.words)          # Word-level timestamps + speaker IDs
```

No streaming variant. This is a synchronous file-upload endpoint (or async with webhooks).

---

## API 4: Speech to Text — Realtime (`client.speech_to_text.realtime`)

**One api_class: `elevenlabs_stt_realtime`** — used by `scribe_v2_realtime`

WebSocket-based. Completely different calling pattern from everything else. Uses `await .connect()` with typed options, returns a `RealtimeConnection` that emits events.

```python
from elevenlabs import ElevenLabs, AudioFormat, CommitStrategy

client = ElevenLabs(api_key="...")

# Option A: Manual audio chunks
connection = await client.speech_to_text.realtime.connect({
    "model_id": "scribe_v2_realtime",           # str, required
    "audio_format": AudioFormat.PCM_16000,       # AudioFormat enum, required
    "sample_rate": 16000,                        # int, required
    "commit_strategy": CommitStrategy.VAD,       # CommitStrategy enum, optional (default: MANUAL)
    "vad_silence_threshold_secs": 1.5,           # float, optional (0.3–3.0)
    "vad_threshold": 0.4,                        # float, optional (0.1–0.9)
    "min_speech_duration_ms": 100,               # int, optional (50–2000)
    "min_silence_duration_ms": 100,              # int, optional (50–2000)
    "language_code": "en",                       # str, optional
    "include_timestamps": True,                  # bool, optional (default: false)
})

# Option B: URL-based streaming
connection = await client.speech_to_text.realtime.connect({
    "model_id": "scribe_v2_realtime",
    "url": "https://stream.example.com/audio.mp3",
    # Same optional params as above
})

# Event-driven usage
from elevenlabs import RealtimeEvents

connection.on(RealtimeEvents.PARTIAL_TRANSCRIPT, lambda data: print(data))
connection.on(RealtimeEvents.COMMITTED_TRANSCRIPT, lambda data: print(data))

# For manual chunks:
await connection.send(audio_bytes)
await connection.commit()  # if commit_strategy is MANUAL
```

**Key difference:** This is async-only, WebSocket-only. Requires `websockets` package. The `AudioFormat` and `CommitStrategy` are SDK enums, not strings.

---

## API 5: Music (`client.music`)

**One api_class: `elevenlabs_music`** — used by `music_v1`

```python
from elevenlabs import ElevenLabs

client = ElevenLabs(api_key="...")

# Simple prompt mode (returns Iterator[bytes])
audio = client.music.compose(
    prompt="An upbeat indie rock song about summer",  # str|None, optional (max 4100 chars)
    model_id="music_v1",                              # literal "music_v1", optional
    output_format="mp3_44100_128",                    # enum, optional
    music_length_ms=120000,                            # int|None, optional (3000–600000)
    force_instrumental=False,                          # bool, optional (prompt mode only)
    seed=42,                                           # int|None, optional (composition_plan only)
    sign_with_c_2_pa=False,                            # bool, optional (mp3 only)
)

# Composition plan mode (mutually exclusive with prompt)
from elevenlabs.types import MusicPrompt
audio = client.music.compose(
    composition_plan=MusicPrompt(...),                 # MusicPrompt|None, optional
    respect_sections_durations=True,                   # bool, optional (composition_plan only)
    # ... same other params
)
```

Also available: `.stream()` (streaming version), `.compose_detailed()` (returns detailed response with section info), `.video_to_music()`, `.upload()`, `.separate_stems()`.

---

## What the dev team needs to build

**4 distinct APIs** (totally different HTTP patterns/SDK modules):

1. **`text_to_speech`** — HTTP POST, voice_id as path param, text in body, returns audio bytes
2. **`text_to_dialogue`** — HTTP POST, list of inputs in body, returns audio bytes
3. **`speech_to_text`** — HTTP POST multipart, file upload, returns JSON transcript
4. **`speech_to_text.realtime`** — WebSocket, async event-driven, bidirectional audio/text
5. **`music`** — HTTP POST, text prompt in body, returns audio bytes

**5 api_classes** (each needs its own parameter handling):

| `api_class` | Why it's distinct |
|---|---|
| `elevenlabs_tts` | Single voice_id path param, VoiceSettings object, request stitching (previous/next text/ids) |
| `elevenlabs_dialogue` | List of DialogueInput objects, no voice_settings, no stitching, max 10 voices |
| `elevenlabs_stt` | File upload, returns JSON not audio, diarization/entity/keyterm params |
| `elevenlabs_stt_realtime` | WebSocket, async-only, AudioFormat/CommitStrategy enums, VAD params, event-driven |
| `elevenlabs_music` | Text prompt OR composition plan (mutually exclusive), music_length_ms, force_instrumental |

**Shared across all:** The ElevenLabs client instantiation is unified — one `ElevenLabs(api_key="...")` instance gives access to all modules via `client.text_to_speech`, `client.text_to_dialogue`, `client.speech_to_text`, `client.music`.