Get word-level timestamps

Timestamps are word, sentence, or utterance timing spans returned together with the audio, for captions and read-along UIs. One request option changes the response shape and the rate.

View as Markdown

Timestamps are timing spans, word, sentence, or utterance, returned together with the audio. Set timestamps on POST /v1/tts to receive them. The response then carries the audio together with timing spans, each holding the span's text, its start, and its end in seconds. Timed requests bill at 1.25 times the model's per-character rate, rounded up to the next hundredth of a credit, minimum 0.1 credit per request.

curl -X POST https://api.paxalabs.com/v1/tts \
  -H "Authorization: Bearer $PAXA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "สวัสดีครับ ยินดีต้อนรับสู่ Paxa Labs",
    "voice": "khanomkrok",
    "model": "paxa-tts-flash-v2",
    "timestamps": "word"
  }' \
  -o response.json

jq -r .audio response.json | base64 -d > speech.mp3
jq .timing.spans response.json

Choose a granularity#

The timestamps value picks how the audio is segmented; every span carries text, start, and end in seconds. Spans cover the spoken text, and an emotion tag takes no span.

ValueSpans
wordOne span per spoken word. The model segments Thai itself, and the text needs no spaces.
sentenceOne span per sentence.
utteranceOne span per continuous run of speech between pauses. An utterance can cover several sentences read in one breath, or part of a long one.

Response format#

With buffered delivery the response is application/json, and the body is no longer binary audio. The audio field carries the complete file as base64. The timing.spans field lists every span at the requested granularity. Decoding audio yields exactly the bytes an untimed request returns, and the usual headers, including x-credits-charged, are unchanged.

Response
{
  "audio": "SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjYy...",
  "timing": {
    "granularity": "word",
    "spans": [
      {
        "text": "สวัสดี",
        "start": 0,
        "end": 0.51
      },
      {
        "text": "ครับ",
        "start": 0.51,
        "end": 0.85
      },
      {
        "text": "ยินดี",
        "start": 0.85,
        "end": 1.27
      },
      {
        "text": "ต้อนรับ",
        "start": 1.27,
        "end": 1.87
      },
      {
        "text": "สู่",
        "start": 1.87,
        "end": 2.12
      },
      {
        "text": "Paxa",
        "start": 2.12,
        "end": 2.46
      },
      {
        "text": "Labs",
        "start": 2.46,
        "end": 2.8
      }
    ]
  }
}

Stream timed audio#

With stream: true the response is application/x-ndjson. Each line is one JSON event carrying a base64 audio chunk and the spans that chunk completed. A final done event carries the total span count. Compare it with the spans you collected. The spans themselves travel exactly once, on the chunk lines. If the stream ends without a done event, treat the request as failed. The charge is refunded automatically, under the same rule as audio streaming.

NDJSON stream
{"type":"chunk","audio":"SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjYy...","spans":[{"text":"สวัสดี","start":0,"end":0.51},{"text":"ครับ","start":0.51,"end":0.85},{"text":"ยินดี","start":0.85,"end":1.27}]}
{"type":"chunk","audio":"SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjYy...","spans":[{"text":"ต้อนรับ","start":1.27,"end":1.87},{"text":"สู่","start":1.87,"end":2.12},{"text":"Paxa","start":2.12,"end":2.46},{"text":"Labs","start":2.46,"end":2.8}]}
{"type":"done","granularity":"word","span_count":7}

Pricing#

The surcharge applies only when timestamps is set. The same request then bills 1.25 times its untimed cost. The 36-character example above costs 0.45 credits timed and 0.36 credits untimed. GET /v1/models lists each model's per-character rate. Rounding and the 0.1 credit minimum work as described in Credits and billing. Try it on your own text in the playground. The model behind timed speech has its own page, text to speech.

Note

The OpenAI-compatible endpoint ignores timestamps, since the OpenAI request shape has no equivalent field. Timing is also available on every live connection, priced the same way.