How to get the timestamp of every word in a recording

Call POST /transcribe with output_format set to json and word_timestamps set to true (both are the default). Every segment in the result includes a words array of {start, end, word} objects with times in seconds. This guide covers the response shape, which models return word times, and what you can build with them.

Get an API key Quickstart Supported languages

Last updated: · Markdown

The request

{
  "audio_url": "https://example.com/interview.mp3",
  "model": "oracle",
  "language": "auto",
  "word_timestamps": true,
  "output_format": "json"
}

Start the job with POST /transcribe, then poll GET /transcribe/{jobId} until it reaches COMPLETED. Always pass model explicitly, because if you omit it, the API defaults to the legacy core model.

The response

{
  "jobId": "34c63d45-4cb5-473b-bc60-25c175e69c57",
  "status": "COMPLETED",
  "result": {
    "language": "en",
    "segments": [
      {
        "start": 1.0,
        "end": 3.75,
        "text": "Hello everyone and welcome to the show.",
        "speaker": "Speaker 1",
        "words": [
          { "start": 1.0, "end": 1.35, "word": "Hello" },
          { "start": 1.4, "end": 1.75, "word": "everyone" }
        ]
      }
    ]
  }
}
  • segments[]: contains start and end in seconds, text, speaker (when diarization is enabled), and words[].
  • words[]: contains start, end, and word, with times in seconds rounded to three decimal places.
  • A short word might only last a few hundredths of a second, and sometimes start and end are identical. Never divide by a word's duration in your code.
  • If you set word_timestamps to false, or use a model that only outputs segment timings, the words array is omitted and you only get start and end on each segment.

Which models return word times

ModelWord times
base, oracle, oracle_pro, oracle_maxYes
oracle_miniReturns segment times for plain transcription, and word times when a speaker mode (diarization or channel separation) is enabled
core, edge (legacy)Yes

Plain text (txt) output does not include timings. SRT files built by the API follow the same switch. See the SRT guide for details.

Find where a phrase is said

You can search the flattened word list to get a time range. That lets an agent answer a question like "where does she mention pricing?" with the time, or pull that sentence out as a clip.

find.py

# find.py — where is a phrase said? Returns (start, end) in seconds.
import re


def find_phrase(result, phrase):
    words = [w for segment in result["segments"] for w in segment.get("words", [])]
    clean = lambda text: re.sub(r"[^\w]", "", text.lower())
    target = [clean(p) for p in phrase.split()]
    tokens = [clean(w["word"]) for w in words]
    for i in range(len(tokens) - len(target) + 1):
        if tokens[i:i + len(target)] == target:
            return words[i]["start"], words[i + len(target) - 1]["end"]
    return None


# start, end = find_phrase(result, "welcome to the show")
# ffmpeg -ss <start> -to <end> -i talk.mp4 -c:v libx264 -c:a aac clip.mp4

Cutting a clip from a time range:

ffmpeg -ss 41.2 -to 47.8 -i talk.mp4 -c:v libx264 -c:a aac clip.mp4

Tighten pauses and jump cuts

Having timestamps for every word lets you measure the silence between them. You can keep continuous speech and cut out any pause longer than your threshold:

silences.py

# silences.py — keep the speech, drop pauses longer than max_gap seconds
def speech_ranges(words, max_gap=0.7, pad=0.1):
    ranges = []
    for word in words:
        if ranges and word["start"] - ranges[-1][1] <= max_gap:
            ranges[-1][1] = word["end"]
        else:
            ranges.append([word["start"], word["end"]])
    return [[max(0.0, s - pad), e + pad] for s, e in ranges]

# Pass the ranges to cut() from cut.py to build the tightened video.

Stitch the remaining ranges together using cut() from the video editing guide.

Other ways to use word timings

  • Read-along highlighting in a web player: highlight whichever word covers the player's currentTime.
  • Clickable transcripts: link each word to ?t=<start> so clicking it jumps the player to that point.
  • Chapters and key moments: have an LLM pick the relevant segment numbers, then use your code to look up their timestamps.
  • Library search: index words alongside their start times so search results open the recording at the exact second.
  • Alignment and corpus work: you can convert the JSON word timings into the annotation formats your tools read.

Pricing

Word timestamps do not change the price: usage is metered per second of audio at the model rate, from US$0.48 per hour on base. One hour on oracle is US$1.44. Rates: /llms/pricing.md.

Questions and answers

How do I get word-level timestamps from a transcription API?

Send your request with output_format: "json" and word_timestamps: true. Every segment in the response will include a words[] array with start, end, and word in seconds.

Which models support word timestamps?

base, oracle, oracle_pro and oracle_max return them. oracle_mini returns segment times unless a speaker mode is on. Text output has no timing.

How accurate are the word times?

KolWrite does not publish per-language accuracy figures for this API. Word times work well for highlighting and cutting between words, though you should test a sample of your own recordings and leave a bit of padding around cut points.

Can I get timestamps for a video file?

Yes. When you upload a video, the API reads its audio track directly. If your video is over 100 MiB, extract the audio with ffmpeg before uploading.

Are timestamps in seconds or milliseconds?

They are in seconds, formatted as numbers with up to three decimal places (like 41.236).