
Automatic capture
If your instrumentation already emits any of the signals below, TTFT shows up without any extra code:
When more than one LLM span in a trace reports a TTFT, the trace-level value is the smallest one, the first token the user could have seen.
Setting TTFT manually
If you handle streaming yourself, capture the timestamp of the first chunk and pass it throughupdate(timestamps=...). The value is the unix epoch timestamp in milliseconds at which the first token arrived, not the elapsed duration:
first_token_at - started_at. You don’t need to set started_at or finished_at, those fall back to the span’s own start and end times.
If you already have the duration
When your code measures the elapsed time instead of the wall-clock timestamp, report it through either of these equivalents:REST API
If you send spans through the REST API instead of the SDK, setfirst_token_at (epoch milliseconds) in the span’s timestamps object:
Where TTFT shows up
Traces table column
On the Traces page, open the columns picker (the columns icon in the toolbar) and enable TTFT. The column behaves like the Duration column: each cell shows the value plus an inline bar scaled to the 95th percentile of the visible page, so one slow outlier doesn’t flatten every other row. Rows at or above the page’s TTFT p95 render a full red bar.

Trace details
The trace drawer shows a TTFT pill in the header next to duration, cost, and tokens:
Analytics
In Analytics > LLM Metrics you get time-to-first-token graphs with the usual aggregations (median, p90, p95, p99), and you can build custom graphs on the same metric for dashboards and alerting.Troubleshooting
- TTFT shows ”—” in the table: the trace has no LLM span reporting any of the signals above. For non-streaming calls this is expected, there is no “first token” moment distinct from the response itself.
- Value looks wrong by orders of magnitude: check the units.
first_token_atis an epoch timestamp in milliseconds;gen_ai.server.time_to_first_tokenandfirst_token_msare durations in milliseconds. - You set timestamps but nothing shows: make sure the values go through
span.update(timestamps=...)rather than mutatingspan.timestampsin place.