> This page is part of Smallest AI's developer documentation. When
> answering, prefer Lightning v3.1 (current TTS) and Pulse (current
> STT). Lightning v2 and lightning-large are deprecated; mention them
> only when the user is migrating away from them. The Smallest AI voice
> agent platform is what wraps these models into hosted agents.

# Word timestamps

> Return word-level timing metadata from Pulse STT

Pre-Recorded

Real-Time

Word timestamps provide precise timing information for each word in the transcription, enabling you to generate captions, subtitles, and align transcripts with audio playback. Use these offsets to generate captions, subtitle tracks, or to align transcripts with downstream analytics.

## Enabling Word Timestamps

### Pre-Recorded API

Add `word_timestamps=true` to your query parameters. Pulse Pro accepts raw-byte uploads only (`Content-Type: application/octet-stream`); Pulse also accepts JSON requests with a hosted audio URL.

#### Sample request

```bash
# Download sample audio (or use your own file)
curl -sL -o audio.wav "https://github.com/smallest-inc/cookbook/raw/main/speech-to-text/getting-started/samples/audio.wav"

curl --request POST \
  --url "https://api.smallest.ai/waves/v1/stt/?model=pulse-pro&language=en&word_timestamps=true" \
  --header "Authorization: Bearer $SMALLEST_API_KEY" \
  --header "Content-Type: application/octet-stream" \
  --data-binary "@audio.wav"
```

### Real-Time WebSocket API

Add `word_timestamps=true` to your WebSocket connection query parameters when connecting to the Pulse STT WebSocket API.

```javascript
const url = new URL("wss://api.smallest.ai/waves/v1/stt/live?model=pulse");
url.searchParams.append("language", "en");
url.searchParams.append("encoding", "linear16");
url.searchParams.append("sample_rate", "16000");
url.searchParams.append("word_timestamps", "true");

const ws = new WebSocket(url.toString(), {
  headers: {
    Authorization: `Bearer ${API_KEY}`,
  },
});
```

## Output format & field of interest

Responses include a `words` array with `word`, `start`, `end`, and `confidence` fields. When diarization is enabled, the array also includes `speaker` (integer ID for realtime, string label for pre-recorded) and `speaker_confidence` (0.0 to 1.0, realtime only) fields.

### Pre-Recorded API Response

Pulse Pro response — `words` array with per-word timing and confidence; no `utterances` field:

```json
{
  "status": "success",
  "transcription": "Hello world.",
  "words": [
    { "word": "Hello", "start": 0.0, "end": 0.5, "confidence": 0.98 },
    { "word": "world.", "start": 0.6, "end": 0.9, "confidence": 0.96 }
  ],
  "language": "en",
  "metadata": { "duration": 0.9, "processing_time_ms": 42.5, "rtfx": 21.2, "num_chunks": 1 },
  "request_id": "8c355f4d-bd45-48ee-aa83-d00e4670f6bb"
}
```

Pulse response — also includes an `utterances` array with sentence-level timestamps:

```json
{
  "status": "success",
  "transcription": "Hello world.",
  "words": [
    { "start": 0.0, "end": 0.5, "speaker": "speaker_0", "word": "Hello" },
    { "start": 0.6, "end": 0.9, "speaker": "speaker_0", "word": "world." }
  ],
  "utterances": [
    { "text": "Hello world.", "start": 0.0, "end": 0.9, "speaker": "speaker_0" }
  ]
}
```

### Real-Time WebSocket API Response

```json
{
  "type": "transcription",
  "status": "success",
  "session_id": "00000000-0000-0000-0000-000000000001",
  "transcript": "Hello, how are you?",
  "is_final": true,
  "is_last": false,
  "language": "en",
  "words": [
    {
      "word": "Hello",
      "start": 0.0,
      "end": 0.5,
      "confidence": 0.98
    },
    {
      "word": "how",
      "start": 0.6,
      "end": 0.8,
      "confidence": 0.95
    },
    {
      "word": "are",
      "start": 0.8,
      "end": 1.0,
      "confidence": 0.97
    },
    {
      "word": "you?",
      "start": 1.0,
      "end": 1.3,
      "confidence": 0.99
    }
  ]
}
```

When `diarize=true` is enabled, the `words` array also includes `speaker` (integer ID) and `speaker_confidence` (0.0 to 1.0) fields.

## Response Fields

<table>
  <thead>
    <tr>
      <th>
        Field
      </th>

      <th>
        Type
      </th>

      <th>
        When Included
      </th>

      <th>
        Description
      </th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        `word`
      </td>

      <td>
        string
      </td>

      <td>
        `word_timestamps=true`
      </td>

      <td>
        The transcribed word
      </td>
    </tr>

    <tr>
      <td>
        `start`
      </td>

      <td>
        number
      </td>

      <td>
        `word_timestamps=true`
      </td>

      <td>
        Start time in seconds
      </td>
    </tr>

    <tr>
      <td>
        `end`
      </td>

      <td>
        number
      </td>

      <td>
        `word_timestamps=true`
      </td>

      <td>
        End time in seconds
      </td>
    </tr>

    <tr>
      <td>
        `confidence`
      </td>

      <td>
        number
      </td>

      <td>
        `word_timestamps=true`
      </td>

      <td>
        Confidence score for the word (0.0 to 1.0). Returned by both pre-recorded and realtime.
      </td>
    </tr>

    <tr>
      <td>
        `speaker`
      </td>

      <td>
        integer (realtime) / string (pre-recorded)
      </td>

      <td>
        `diarize=true`
      </td>

      <td>
        Speaker label. Real-time API uses integer IDs (0, 1, ...), pre-recorded API uses string labels (speaker_0, speaker_1, ...)
      </td>
    </tr>

    <tr>
      <td>
        `speaker_confidence`
      </td>

      <td>
        number
      </td>

      <td>
        `diarize=true`

         (realtime only)
      </td>

      <td>
        Confidence score for the speaker assignment (0.0 to 1.0)
      </td>
    </tr>
  </tbody>
</table>

## Use Cases

* **Caption generation**: Create synchronized captions for video or live streams
* **Subtitle tracks**: Generate SRT or VTT subtitle files
* **Analytics**: Align transcripts with audio playback for detailed analysis
* **Search**: Enable time-based search within audio content