---
title: 'Model API reference'
description: 'Call models through one OpenAI-compatible endpoint, with streaming, tool calls, embeddings, moderation, transcription, images and video.'
url: 'https://docs.streamtoken.dev/api-reference'
last_updated: '2026-11-11'
---

> Docs index: https://docs.streamtoken.dev/llms.txt · MCP: https://docs.streamtoken.dev/mcp · Skill: https://docs.streamtoken.dev/skill.md

# Model API reference

Token Streamer gives you one OpenAI-compatible API for the models you connect. Send requests to `https://router.streamtoken.dev/v1` with an [API key](https://docs.streamtoken.dev/api-keys.md).

## Authentication

Send your key as a Bearer token on every request:

```bash
curl https://router.streamtoken.dev/v1/models \
  -H "Authorization: Bearer $STREAMTOKEN_API_KEY"
```

A missing, expired or revoked key returns HTTP 401 `invalid_api_key`. A key can use only the models it was given, in the region it was issued for.

## Use the OpenAI SDK

Point any OpenAI SDK at the base URL and use your Token Streamer key.

```python
from openai import OpenAI

client = OpenAI(
    base_url="https://router.streamtoken.dev/v1",
    api_key="<STREAMTOKEN_API_KEY>",
)

completion = client.chat.completions.create(
    model="openai/byok/gpt-5.4-mini",
    messages=[{"role": "user", "content": "Reply with a greeting."}],
)
print(completion.choices[0].message.content)
```

```ts
import OpenAI from 'openai';

const client = new OpenAI({
    baseURL: 'https://router.streamtoken.dev/v1',
    apiKey: process.env.STREAMTOKEN_API_KEY,
});

const completion = await client.chat.completions.create({
    model: 'openai/byok/gpt-5.4-mini',
    messages: [{ role: 'user', content: 'Reply with a greeting.' }],
});
console.log(completion.choices[0].message.content);
```

## List models and providers

`GET /v1/models` lists the models your key can use. `GET /v1/providers` lists the providers behind them. Results are limited to your key's permitted models, your saved provider connections and the key's region. Neither call starts model inference.

```bash
curl https://router.streamtoken.dev/v1/providers \
  -H "Authorization: Bearer $STREAMTOKEN_API_KEY"
```

This example key permits one OpenAI model in Europe:

```json title="docs/examples/providers-response.json"
{
    "object": "list",
    "region": "europe",
    "checked_at": null,
    "data": [
        {
            "id": "openai",
            "object": "provider",
            "name": "OpenAI",
            "model_count": 1,
            "models": ["openai/byok/gpt-5.4-mini"],
            "provider_status": "unknown"
        }
    ]
}
```

Provider status comes from the provider's public status feed. An open incident that mentions the provider's API makes it `degraded`, or `unavailable` for a full or major outage. Providers without a public feed (Mistral, xAI, Google) are `unknown`, and so is any status older than 15 minutes.

List one provider's models:

```bash
curl https://router.streamtoken.dev/v1/providers/openai/models \
  -H "Authorization: Bearer $STREAMTOKEN_API_KEY"
```

```json title="docs/examples/provider-models-response.json"
{
    "object": "list",
    "data": [
        {
            "id": "openai/byok/gpt-5.4-mini",
            "object": "model",
            "created": 0,
            "owned_by": "openai"
        }
    ]
}
```

These identifiers can be passed to chat completions. A provider outside your key's permitted routes returns HTTP 404 with the `provider_not_found` error code. Other HTTP methods return HTTP 405. Responses are not cached. The [Providers page](https://docs.streamtoken.dev/providers.md) shows request counts and timing for each provider.

## Model identifiers and billing

Identifiers containing `/byok/` use your own provider connection: the provider bills your account, and the response header `X-StreamToken-Billing-Mode` is `byok`. Token Streamer records measured tokens and an estimate from its reference rates, with a Token Streamer charge of zero. Estimates exclude cache adjustments and account-specific discounts.

Identifiers without `/byok/`, such as `openai/gpt-5.4-mini`, use credits from your organization wallet. You do not supply a provider key for them. If your balance is too low, the request returns HTTP 402 (see [errors](https://docs.streamtoken.dev/api-reference#errors)).

## Send a completion

`POST /v1/chat/completions`. Save this request as `chat-request.json`:

```json title="docs/examples/chat-request.json"
{
    "model": "openai/gpt-5.4-mini",
    "messages": [
        {
            "role": "user",
            "content": "Reply with a greeting."
        }
    ],
    "max_completion_tokens": 64,
    "stream": false
}
```

```bash
curl https://router.streamtoken.dev/v1/chat/completions \
  -H "Authorization: Bearer $STREAMTOKEN_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: completion-example-0001' \
  --data-binary @chat-request.json
```

The response looks like this. Text, IDs, timestamps and token counts vary.

```json title="docs/examples/chat-response.json"
{
    "id": "chatcmpl-fixture",
    "object": "chat.completion",
    "created": 1,
    "model": "openai/gpt-5.4-mini",
    "choices": [
        {
            "index": 0,
            "finish_reason": "stop",
            "message": {
                "role": "assistant",
                "content": "Fixture provider response"
            }
        }
    ],
    "usage": {
        "prompt_tokens": 10,
        "completion_tokens": 2,
        "total_tokens": 12
    }
}
```

Usage is settled at the price version locked before the request is sent. `X-StreamToken-Accounting` is `settled` or `sync_pending` for a non-streaming completion. A repeated `Idempotency-Key` returns `409 request_exists` and does not call the provider again.

## Stream a completion

Set `"stream": true`. Save this request as `stream-request.json`:

```json title="docs/examples/stream-request.json"
{
    "model": "openai/gpt-5.4-mini",
    "messages": [
        {
            "role": "user",
            "content": "Reply with a greeting."
        }
    ],
    "max_completion_tokens": 64,
    "stream": true,
    "stream_options": {
        "include_usage": true
    }
}
```

```bash
curl --no-buffer https://router.streamtoken.dev/v1/chat/completions \
  -H "Authorization: Bearer $STREAMTOKEN_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: streaming-example-0001' \
  --data-binary @stream-request.json
```

The response has content type `text/event-stream`. Events arrive separated by blank lines. One network read can hold part of an event or several events, so parse the SSE framing before parsing JSON.

```text title="docs/examples/stream-response.txt"
data: {"id":"chatcmpl-stream-fixture","object":"chat.completion.chunk","created":1,"model":"openai/gpt-5.4-mini","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}

data: {"id":"chatcmpl-stream-fixture","object":"chat.completion.chunk","created":1,"model":"openai/gpt-5.4-mini","choices":[{"index":0,"delta":{"content":"Hello 🌍"},"finish_reason":null}]}

data: {"id":"chatcmpl-stream-fixture","object":"chat.completion.chunk","created":1,"model":"openai/gpt-5.4-mini","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: {"id":"chatcmpl-stream-fixture","object":"chat.completion.chunk","created":1,"model":"openai/gpt-5.4-mini","choices":[],"usage":{"prompt_tokens":10,"completion_tokens":2,"total_tokens":12}}

data: [DONE]
```

`stream_options.include_usage: true` adds a final chunk with empty `choices` and the measured usage. The stream ends with `data: [DONE]` only after a valid finish and valid final usage.

### Handle interruptions

Errors before streaming starts return a JSON error and a non-success status. A provider rejection returns HTTP 502 `provider_rejected` and, when the provider answered, a numeric `error.provider_status` such as 401, 403 or 429. Raw provider errors and credentials are never forwarded.

Once streaming starts, the HTTP status cannot change. If the provider stream is interrupted, the router sends a `data: {"error": ...}` event (customer-owned routes use `stream_interrupted`) and closes the stream without `[DONE]`. A client that disconnects can miss that event. Never treat the end of a connection alone as success. The router does not retry inference, switch providers or guess a charge. Use the `X-Request-Id` header to look up the outcome in [Diagnostics](https://docs.streamtoken.dev/diagnostics.md).

Streams wait up to 10 seconds for provider headers and 10 seconds for each read, and last at most 60 seconds.

## Receive function calls

Function tools work with `stream: true`. The router forwards your tool definitions and streams the model's proposed calls. Your application authorizes and runs the tool; the router does not.

```json title="docs/examples/tool-request.json"
{
    "model": "openai/gpt-5.4-mini",
    "messages": [
        {
            "role": "user",
            "content": "Reply with a greeting."
        }
    ],
    "max_completion_tokens": 64,
    "stream": true,
    "stream_options": {
        "include_usage": true
    },
    "tools": [
        {
            "type": "function",
            "function": {
                "name": "weather",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "city": {
                            "type": "string"
                        }
                    },
                    "required": ["city"],
                    "additionalProperties": false
                },
                "strict": true
            }
        }
    ],
    "tool_choice": "required"
}
```

The model streams argument fragments like these:

```text title="docs/examples/tool-response.txt"
data: {"id":"chatcmpl-stream-fixture","object":"chat.completion.chunk","created":1,"model":"openai/gpt-5.4-mini","choices":[{"index":0,"delta":{"role":"assistant","tool_calls":[{"index":0,"id":"call_weather","type":"function","function":{"name":"weather","arguments":""}}]},"finish_reason":null}]}

data: {"id":"chatcmpl-stream-fixture","object":"chat.completion.chunk","created":1,"model":"openai/gpt-5.4-mini","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"city\":"}}]},"finish_reason":null}]}

data: {"id":"chatcmpl-stream-fixture","object":"chat.completion.chunk","created":1,"model":"openai/gpt-5.4-mini","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"Paris\"}"}}]},"finish_reason":null}]}

data: {"id":"chatcmpl-stream-fixture","object":"chat.completion.chunk","created":1,"model":"openai/gpt-5.4-mini","choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}]}

data: {"id":"chatcmpl-stream-fixture","object":"chat.completion.chunk","created":1,"model":"openai/gpt-5.4-mini","choices":[],"usage":{"prompt_tokens":10,"completion_tokens":2,"total_tokens":12}}

data: [DONE]
```

Accumulate `function.arguments` by `tool_calls[index]` until the choice finishes. Parse the assembled JSON and validate it against your tool schema before running anything. A `strict` declaration does not replace your own authorization checks.

For the next turn, send the assistant message with the assembled `tool_calls`, then a `tool` message for each call with its `tool_call_id` and a string result. Every call needs a result before the next request. Use a new idempotency key. OpenAI routes stream function calls; Anthropic routes stream text.

## Create embeddings

`POST /v1/embeddings` turns text into vectors. Fields: `model`, `input` (one text, or 1 to 128 non-empty texts of at most 32,768 characters each), `encoding_format` (`float` or `base64`) and `dimensions` (OpenAI models only). Other fields return `invalid_request`.

```json
{
    "model": "openai/byok/text-embedding-3-small",
    "input": ["first text", "second text"]
}
```

The response has `object: "list"`, one `data` item per input in order, the `model` ID and `usage.prompt_tokens`. Chat models are refused here, and embedding models are refused by chat completions, with `model_not_found`. Request text is never stored.

## Classify content

`POST /v1/moderations` checks text. Fields: `model` and `input` (one text, or 1 to 32 non-empty texts of at most 32,768 characters each). Images and other fields return `invalid_request`.

```json
{ "model": "openai/byok/omni-moderation-latest", "input": "Text to check" }
```

The response has an `id`, the `model` ID and one `results` item per input, in order. Each has `flagged`, `categories` and `category_scores` (0 to 1). Category names come from the provider. Mistral does not return `flagged`, so the router sets it to true when any category is true. Moderation is free from both providers, so usage rows have no token counts or cost.

## Transcribe audio

`POST /v1/audio/transcriptions` turns audio into text. Send `multipart/form-data` with a `Content-Length` header and these fields: `model`, `file` (up to 25 MB; mp3, mp4, mpeg, mpga, m4a, wav or webm), and optionally `language` (a two-letter ISO-639-1 code) and `prompt` (up to 2,000 characters).

```bash
curl https://router.streamtoken.dev/v1/audio/transcriptions \
  -H "Authorization: Bearer $STREAMTOKEN_API_KEY" \
  -F model=openai/byok/gpt-transcribe -F file=@meeting.m4a
```

The response is `{ "text": "...", "model": "...", "usage": { "type": "duration", "seconds": 61.2 } }`. `usage` is null when the provider does not report a duration. The estimate is the per-minute price times the audio length, rounded up. Neither the audio nor the text is stored.

## Generate images

`POST /v1/images/generations` creates images from a prompt. Fields: `model`, `prompt` (1 to 32,000 characters), `n` (1 to 4), `size` (`auto` or `WIDTHxHEIGHT`), `quality` (`low`, `medium`, `high`, `xhigh`, `max` or `auto`) and `output_format` (`png`, `jpeg` or `webp`). Image edits, input images and URL results are not supported.

```json
{
    "model": "openai/byok/gpt-image-2.6-flare",
    "prompt": "A red kite over a beach",
    "size": "1024x1024",
    "quality": "low"
}
```

The response has `created`, the `model` ID, one `data` item per image with `b64_json` (and `revised_prompt` when the provider sends one), and `usage` with `input_tokens` and `output_tokens`. Neither the prompt nor the images are stored. The **Test** button generates one 1024x1024 low-quality image, which your provider bills to your account.

## Generate videos

`POST /v1/videos/generations` creates one video with sound from a prompt. Fields: `model`, `prompt` (1 to 32,000 characters), `seconds` (3 to 15, default 10), `resolution` (`360p`, `720p` or `1080p`, default `720p`) and `aspect_ratio` (`16:9` or `9:16`). Input images, reference videos, editing, extension and 4K are not supported.

```json
{
    "model": "google/byok/gemini-omni-1.2-flash",
    "prompt": "A red kite over a beach",
    "seconds": 5,
    "resolution": "720p"
}
```

The response has `created`, the `model` ID, one `data` item with `b64_json` and `mime_type` (`video/mp4`), and `usage`. The call waits until the video is ready, which can take a few minutes; it gives up after 5 minutes. Videos use your own Google Gemini key. The estimate uses $1.50 input and $12 output per 1M tokens; a 10 second 720p clip is about $0.70. The router asks the provider not to store the request. Neither the prompt nor the video is stored by Token Streamer. The **Test** button generates one 3 second 360p clip, about $0.07, billed to your provider account.

## Choose a region

Organization owners choose the provider region in **Models**. Europe supports the enabled OpenAI and Mistral routes. The United States also supports the Claude 4.6-and-later routes and the supported xAI routes. Availability depends on the model, your account and the regional endpoint. A missing regional credential or unavailable model stops the request before it is sent. This setting covers the provider path only.

Customer-owned routes accept up to four inline base64 images and a request body up to 512 KiB.

## Find an alternative model

`GET /v1/model-availability` lists the customer-owned routes your key can use. Each result has `id`, `provider`, `capabilities`, `provider_status` and `observed_status`, plus the region and the snapshot's `checked_at` time. Missing or expired snapshots return `unknown`.

Each result also declares `context_tokens`, `max_output_tokens` (the model's own output limit), `structured_output` (`false` for every route), `usage_reporting` (`true`) and `processing_location`. `processing_location` is `verified` when the provider reports where inference ran and the router checks it (Claude in the United States), `unknown` when it cannot check, and `unsupported` when the route is not available in the region.

Structured output is not supported: a request with `response_format` returns HTTP 400 `unsupported_parameter` with `param: "response_format"`.

To find alternatives to a model, pass `model` and optionally a comma-separated `requires` list (`image_input`, `streaming`, `function_tools`). Routes that are degraded or unavailable are left out. An `unknown` status is not a guarantee. You choose the model for your next request.

## Fall back to other models

On a customer-owned route, send `X-StreamToken-Fallback-Models` with one to three distinct alternative model IDs, in order. For example: `X-StreamToken-Fallback-Models: anthropic/byok/claude-haiku-4.5, openai/byok/gpt-5.6-luna`.

When fresh health data marks the requested route unavailable, the router uses the first alternative that is healthy and allowed in your region. Routes without function tools are skipped for requests that use tools. If no alternative qualifies, the request returns `503 model_unavailable` before any provider is called. If health data is unknown or expired, the requested model is used.

A successful switch sets `X-StreamToken-Selected-Model` to the model used. The response and the [Usage](https://docs.streamtoken.dev/usage-history.md) record name it too. Check the alternatives' rates first. The router makes one attempt and does not retry on a different model if the attempt fails or the stream is interrupted.

## Supported parameters

| Field | Behavior |
| --- | --- |
| `model` | Required. Use an identifier permitted for your key. |
| `messages` | 1-100 messages. Text roles: `system`, `developer`, `user`, `assistant`. Streaming also accepts assistant function-call history and matching `tool` results. |
| `max_completion_tokens` | Optional integer, from 1 up to the model's own output limit (`max_output_tokens` in `GET /v1/model-availability`). A larger value returns HTTP 400. `max_tokens` is accepted as an alias. Send one field. |
| `stream` | Optional boolean, default false. |
| `stream_options` | Streaming only. The only setting is boolean `include_usage`. |
| `tools` | Streaming only. 1-16 uniquely named function tools with object `parameters`, optional `description` and boolean `strict`. |
| `tool_choice` | With tools: `auto`, `none`, `required`, or a named declared function. |

The JSON request body is limited to 64 KiB on credit-funded routes. Function names use 1-64 ASCII letters, digits, underscores or hyphens. Call IDs are at most 128 characters and argument strings at most 32,768 characters. Multiple choices, sampling settings and the Responses API are not supported. Unsupported settings are rejected, not ignored. This is a subset of the OpenAI API, not complete compatibility.

## Errors

An insufficient-credit request returns HTTP `402`:

```json title="docs/examples/insufficient-credits.json"
{
    "error": {
        "message": "There are not enough test credits for the maximum request reservation.",
        "type": "invalid_request_error",
        "param": null,
        "code": "insufficient_credits"
    }
}
```

| Result | Meaning |
| --- | --- |
| `400 invalid_request` / `unsupported_parameter` | Fix the request and resend. Nothing was sent to a provider. |
| `401 invalid_api_key` | Use an active key. |
| `403 billing_review_required` | New requests are paused while billing is reviewed. Contact support. |
| `404 model_not_found` | The model is not available for this key. |
| `409 request_exists` | You already used this idempotency key. The provider was not called again, and the original is not replayed. |
| `502 provider_rejected` | The provider rejected the request. No usage was charged. |
| `502 reconciliation_pending` or an SSE error | The outcome is uncertain, and credits stay held until it is resolved. Look the request up in Diagnostics. |
| `503 accounting_unavailable` | The charge could not be reserved, so the request was not sent. Retry. |

Useful response headers:

- `X-Request-Id` identifies the HTTP request and changes on each retry.
- `X-StreamToken-Operation-Id` identifies the credit operation. Look it up in [Diagnostics](https://docs.streamtoken.dev/diagnostics.md).
- `X-StreamToken-Price-Version` identifies the rates locked for the request.

Token Streamer records the outcome, whether the request streamed, its duration and first-output timing. It does not receive your prompts, completions, function arguments or provider credentials in these reports. Held credits are marked **Needs review** until resolved. See [credits](https://docs.streamtoken.dev/credits.md) for balances and recovery.

## Data retention

Owners and administrators can download request metadata from **Usage** and provider key activity from **Models**. Each export is limited to 10,000 records and leaves out credentials, prompts and replies. The retention setting in **Usage** applies to both. Older records are hidden at once and later removed. See [data controls](https://docs.streamtoken.dev/data-controls.md).
