> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sunbird.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Text to Speech

> Convert text into natural-sounding speech

The Text to Speech (TTS) service converts written text into spoken audio. The unified **`POST /tasks/audio/speech`** endpoint exposes two models:

* **`orpheus-3b-tts`** (default) — multilingual, multi-speaker. Voices are catalog tags (e.g. `salt_lug_0001`); list them with `GET /tasks/voice/speakers`.
* **`spark-tts`** — the six fixed Ugandan voices below; supports streaming on Modal.

<Warning>
  **Migrating?** The legacy `/tasks/tts`, `/tasks/modal/tts`, `/tasks/runpod/tts`, `/tasks/modal/orpheus/tts`, streaming (`/stream`, `/stream-with-url`), Orpheus batch, speaker-listing, and `refresh-url` endpoints are **deprecated**. Use the unified `/tasks/audio/speech` endpoints below.
</Warning>

## Single Synthesis (orpheus-3b-tts, default)

### Parameters

| Parameter  | Type   | Required | Description                                            |
| :--------- | :----- | :------- | :----------------------------------------------------- |
| `text`     | string | Yes      | The text to convert to speech.                         |
| `model`    | string | No       | `orpheus-3b-tts` (default) or `spark-tts`.             |
| `voice`    | string | Yes      | Catalog tag (see `GET /tasks/voice/speakers`).         |
| `language` | string | Yes      | Required for `orpheus-3b-tts` — ISO code or full name. |

<CodeGroup>
  ```python Python theme={null}
  import os
  import requests
  from dotenv import load_dotenv

  load_dotenv()

  url = "https://api.sunbird.ai/tasks/audio/speech"
  access_token = os.getenv("AUTH_TOKEN")

  headers = {
      "accept": "application/json",
      "Authorization": f"Bearer {access_token}",
      "Content-Type": "application/json",
  }

  payload = {
      "text": "I am a nurse who takes care of many people.",
      "model": "orpheus-3b-tts",   # default (Optional)
      "voice": "salt_lug_0001",     # catalog tag; see GET /tasks/voice/speakers
      "language": "eng",            # required for orpheus-3b-tts (ISO code or full name)
  }

  response = requests.post(url, headers=headers, json=payload)
  print(response.json())
  ```
</CodeGroup>

### orpheus-3b-tts: Languages and Voices

Speaker IDs encode both the source corpus (`salt_*`, `waxal_*`, `slr32_*`, `slr129_*`, `bateesa_*`) and the language. Languages marked with an em dash are present in the model's training mix but do **not** currently expose individual voice IDs in this checkpoint.

| Config | Language     | Region                 | Speaker IDs                                                                             |
| ------ | ------------ | ---------------------- | --------------------------------------------------------------------------------------- |
| `ach`  | Acholi       | Uganda, South Sudan    | `salt_ach_0001`, `waxal_ach_0001`, `waxal_ach_0005`, `waxal_ach_0006`, `waxal_ach_0008` |
| `afr`  | Afrikaans    | South Africa, Namibia  | `slr32_afr_0009`                                                                        |
| `eng`  | English      | (control language)     | `salt_eng_0001`, `salt_eng_0002`, `salt_eng_0003`                                       |
| `ewe`  | Ewe          | Ghana, Togo            | `slr129_ewe_0001`                                                                       |
| `ful`  | Fulah        | West Africa (Sahel)    | `waxal_ful_0003`, `waxal_ful_0004`, `waxal_ful_0006`                                    |
| `hau`  | Hausa        | Nigeria, Niger, Chad   | `waxal_hau_0004`, `waxal_hau_0006`, `waxal_hau_0007`, `waxal_hau_0008`                  |
| `ibo`  | Igbo         | Nigeria                | `waxal_ibo_0003`, `waxal_ibo_0005`, `waxal_ibo_0008`                                    |
| `kik`  | Kikuyu       | Kenya                  | `waxal_kik_0003`, `waxal_kik_0004`                                                      |
| `kin`  | Kinyarwanda  | Rwanda                 | `bateesa_kin_0001`                                                                      |
| `lgg`  | Lugbara      | Uganda, DRC            | —                                                                                       |
| `lin`  | Lingala      | DRC, Republic of Congo | `slr129_lin_0001`                                                                       |
| `lug`  | Luganda      | Uganda                 | `salt_lug_0001`, `waxal_lug_0002`–`waxal_lug_0008`                                      |
| `luo`  | Luo (Dholuo) | Kenya, Tanzania        | `waxal_luo_0001`–`waxal_luo_0004`                                                       |
| `nyn`  | Runyankole   | Uganda                 | `salt_nyn_0001`, `waxal_nyn_0003`, `waxal_nyn_0004`, `waxal_nyn_0007`, `waxal_nyn_0008` |
| `sot`  | Sesotho      | Lesotho, South Africa  | —                                                                                       |
| `swa`  | Swahili      | East Africa            | `waxal_swa_0006`, `waxal_swa_0007`                                                      |
| `teo`  | Ateso        | Uganda, Kenya          | `salt_teo_0001`                                                                         |
| `tsn`  | Setswana     | Botswana, South Africa | —                                                                                       |
| `xho`  | Xhosa        | South Africa           | `slr32_xho_0012`                                                                        |
| `yor`  | Yoruba       | Nigeria, Benin         | `waxal_yor_0002`, `waxal_yor_0006`, `waxal_yor_0008`                                    |

<Tip>
  Per-language quality scales with the amount of training data collected for that language. **Audition the voices** before relying on a particular speaker — use the discovery snippet under [Listing Voices](#listing-voices).
</Tip>

## Single Synthesis (spark-tts, fixed voices)

```python theme={null}
payload = {
    "text": "I am a nurse who takes care of many people.",
    "model": "spark-tts",
    "voice": "luganda_female",   # voice name, or the numeric id as a string e.g. "248"
    "response_mode": "url",       # "url" (default), "stream", or "both"
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())
```

### spark-tts Voices

| Voice name          | ID  | Language   | Gender |
| :------------------ | :-- | :--------- | :----- |
| `acholi_female`     | 241 | Acholi     | Female |
| `ateso_female`      | 242 | Ateso      | Female |
| `runyankore_female` | 243 | Runyankore | Female |
| `lugbara_female`    | 245 | Lugbara    | Female |
| `swahili_male`      | 246 | Swahili    | Male   |
| `luganda_female`    | 248 | Luganda    | Female |

### Response Modes

`response_mode` applies to **spark-tts on Modal**:

* `url` — generate audio, upload to GCP, return a signed URL (valid \~30 minutes) — default
* `stream` — stream raw audio chunks directly
* `both` — stream audio **and** return a final signed URL

## Listing Voices

```python theme={null}
auth = {"Authorization": f"Bearer {access_token}"}

# Orpheus voices grouped by language (default)
print(requests.get("https://api.sunbird.ai/tasks/voice/speakers", headers=auth).json())

# spark-tts fixed voices
print(requests.get(
    "https://api.sunbird.ai/tasks/voice/speakers",
    headers=auth,
    params={"model": "spark-tts"},
).json())
```

## Batch Synthesis (orpheus-3b-tts)

Synthesize up to 128 items in a single request:

```python theme={null}
url = "https://api.sunbird.ai/tasks/audio/speech/batch"
payload = {
    "items": [
        {"text": "Good morning.", "voice": "salt_lug_0001"},
        {"text": "How are you?", "voice": "salt_eng_0001"},
    ]
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())
```

## Refreshing an Expired URL

Signed URLs expire after \~30 minutes. Re-sign a stored object with `GET /tasks/audio/speech/url`:

```python theme={null}
print(requests.get(
    "https://api.sunbird.ai/tasks/audio/speech/url",
    headers={"Authorization": f"Bearer {access_token}"},
    params={"gcs_object": "orpheus_tts/2026-06-03/abc.wav"},
).json())
```

## Response

```json theme={null}
{
  "audio_url": "https://storage.googleapis.com/.../tts_audio/....wav?...",
  "model": "orpheus-3b-tts",
  "platform": "modal",
  "voice": "salt_lug_0001",
  "audio_url_expires_at": "2026-06-03T22:59:36.954061Z",
  "language": "lug",
  "sample_rate": 24000,
  "duration_seconds": 4.0,
  "gcs_object": "orpheus_tts/2026-06-03/....wav",
  "request_id": "0f1e2d3c4b5a...",
  "timings_ms": {"inference_ms": 1820.5, "upload_ms": 234.1, "total_ms": 2095.6}
}
```

<Note>
  The `audio_url` is a temporary signed URL valid for \~30 minutes. Download or stream the audio promptly, or re-sign it with `GET /tasks/audio/speech/url`.
</Note>
