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

# Long-form narration

> Generate a multi-paragraph narration from a script — letting the SDK handle the 300-character API limit for you.

This example takes a multi-paragraph script and produces a single audio file. It demonstrates the SDK's automatic chunking: you pass the whole script to `create_speech`, the SDK splits at sentence boundaries, generates each segment, and merges the result.

## Python

```python theme={"dark"}
import os
from supertone import Supertone

VOICE_ID = "20160a4c5ba38967330c84"  # replace with your voice ID

SCRIPT = """
Chapter one. The clocktower struck midnight, and the wind through the old
square carried whispers from the workshops below. Hana adjusted her coat,
checked the address one more time, and stepped through the iron gate.

Inside, the air smelled of copper and lemon polish. Rows of half-finished
automatons stared back from the shelves, each one waiting for a name. She
set her satchel on the bench and opened her notebook to a fresh page.

By dawn, the room had changed. One of the figures by the window was no
longer half-finished, and Hana, very quietly, was no longer alone.
""".strip()

with Supertone(api_key=os.environ["SUPERTONE_API_KEY"]) as client:
    response = client.text_to_speech.create_speech(
        voice_id=VOICE_ID,
        text=SCRIPT,
        language="en",
        model="sona_speech_2",
        voice_settings={"pitch_variance": 0.9, "speed": 0.95},
    )

    with open("narration.wav", "wb") as f:
        f.write(response.result.read())

print("Saved narration.wav")
```

## TypeScript

```typescript theme={"dark"}
import { Supertone } from "@supertone/supertone";
import * as fs from "node:fs";

const VOICE_ID = "20160a4c5ba38967330c84"; // replace with your voice ID

const SCRIPT = `
Chapter one. The clocktower struck midnight, and the wind through the old
square carried whispers from the workshops below. Hana adjusted her coat,
checked the address one more time, and stepped through the iron gate.

Inside, the air smelled of copper and lemon polish. Rows of half-finished
automatons stared back from the shelves, each one waiting for a name. She
set her satchel on the bench and opened her notebook to a fresh page.

By dawn, the room had changed. One of the figures by the window was no
longer half-finished, and Hana, very quietly, was no longer alone.
`.trim();

const client = new Supertone({ apiKey: process.env.SUPERTONE_API_KEY });

const response = await client.textToSpeech.createSpeech({
  voiceId: VOICE_ID,
  apiConvertTextToSpeechUsingCharacterRequest: {
    text: SCRIPT,
    language: "en",
    model: "sona_speech_2",
    voiceSettings: { pitchVariance: 0.9, speed: 0.95 },
  },
});

if (response.result instanceof Uint8Array) {
  fs.writeFileSync("narration.wav", response.result);
} else if (response.result && "getReader" in response.result) {
  const reader = (response.result as ReadableStream<Uint8Array>).getReader();
  const chunks: Uint8Array[] = [];
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    if (value) chunks.push(value);
  }
  fs.writeFileSync("narration.wav", Buffer.concat(chunks));
}

console.log("Saved narration.wav");
```

## What happens under the hood

1. The SDK detects that `SCRIPT.length > 300`.
2. It splits on sentence punctuation first, then word boundaries if a sentence is itself too long.
3. The Python SDK fires up to 3 parallel `create_speech` requests; the TypeScript SDK runs them sequentially.
4. Each segment returns a complete audio file.
5. The SDK strips the WAV header from every segment after the first and concatenates the bytes into a single continuous clip.
6. You get one playable file back, identical in form to a single-segment response.

## Tips

* **Punctuation pays off.** Well-punctuated source text produces cleaner cuts. If your script comes from machine translation or transcription, adding `.`/`?`/`!` improves the result.
* **Voice settings travel.** The same `voice_settings` are applied to every segment, so the merged audio sounds consistent.
* **Estimate first.** `predict_duration` doesn't auto-chunk, but you can split your script into a few sentences, call `predict_duration` on each, and sum the durations to estimate cost.
* **Pick the right model.** For long narration, `sona_speech_2` produces the most natural delivery. Switch to `sona_speech_2_flash` if you need to generate many narrations quickly.

## Related

<CardGroup cols={2}>
  <Card title="Long text" icon="align-left" href="/en/docs/text-to-speech/long-text">
    Full reference on the 300-character limit and chunking behavior.
  </Card>

  <Card title="Voice settings" icon="sliders" href="/en/docs/text-to-speech/voice-settings">
    Tune the delivery of your narration.
  </Card>
</CardGroup>
