Speko Docs

Usage

The usage object, how each kind is metered, and how budgets behave on streams.

The usage object

Successful responses and stream usage.updated / terminal frames carry a usage object. All fields are optional and never negative:

{
  "duration_ms": 61000,
  "characters": 512,
  "input_tokens": 1800,
  "cached_input_tokens": 900,
  "output_tokens": 240,
  "reasoning_tokens": 60
}
FieldKindMeaning
duration_msSTTAudio duration transcribed
charactersTTSCharacters accepted for synthesis
input_tokensLLMFresh (uncached) input tokens
cached_input_tokensLLMInput tokens served from provider cache
output_tokensLLMVisible output tokens
reasoning_tokensLLMHidden reasoning tokens

When a provider reports split usage, the lines are mutually exclusive: cached_input_tokens are not double-counted inside input_tokens, and reasoning_tokens are not inside output_tokens. If a provider does not report a reasoning split, its inclusive output count remains in output_tokens; Relay does not estimate hidden tokens.

How each kind meters

  • STT — parsed audio duration, rounded up to whole seconds. Derived from the audio itself, never from a caller declaration.
  • TTS — characters at the moment input is accepted. One-shot requests accept the whole input; streaming sessions accept per input.append. Cancelling later does not refund accepted characters.
  • LLM — provider-reported token counts, normalized where the provider exposes a cache or reasoning split. max_output_tokens bounds reasoning plus visible output.

Admission ceilings vs. billed usage

Before work starts, Speko computes a conservative ceiling (the most the request could cost) and admits the request against your credit. You are billed for reported usage, not the ceiling. For LLM requests the output ceiling is exactly your max_output_tokens — another reason it is required.

Budgets on streams

Long-lived streams extend their budgets as they run:

  • Streaming TTS admits an initial character budget and tops it up transparently as you append text.
  • Streaming STT admits an initial duration slice and renews it while audio flows.

When an extension is refused — credit exhausted, or the session's lease could not be renewed — the stream terminates with a final error frame carrying budget_exhausted or lease_expired. These two codes never appear as HTTP statuses; they exist only on already-established streams.

Correlating with your invoice

Every response and stream carries Speko-Request-ID and per-attempt Speko-Attempt-ID values. Usage is recorded per attempt with prices frozen at attempt start, so a failover mid-request produces attempts that are individually priced and individually visible in the dashboard's activity view.

For a BYOK route, the upstream provider bills your account and Speko does not charge provider usage for that attempt.

On this page