# Models and routing

Each model on llm.buzz is served by one or more providers. You name the model; llm.buzz picks a provider, moves to the next one when a provider fails, and follows your preferences when you give them.

## Model names

A model's name is `author/slug`, such as `qwen/qwen3.8-27b`, or a bare slug. Names use lowercase letters and digits joined by `.`, `_`, `:` or `-`, and are matched exactly. A variant such as `:thinking` is part of the name.

`GET https://api.llm.buzz/api/v1/models` lists the models your key can call. `GET https://api.llm.buzz/api/v1/models/{author}/{slug}/endpoints` lists one model's providers, each with its prices, limits and `tag`; for a bare name it is `/models/{name}/endpoints`. Both answer without a key too, for the public models.

## Suffixes

A suffix after the name changes one request:

| Suffix | Effect |
|---|---|
| `:floor` | The cheapest provider first. |
| `:nitro` | The fastest provider first, by recent time to first token. |
| `:exacto` | Accepted for compatibility, and ignored. |
| `:online` | Asks for a web search on Chat Completions and Responses. llm.buzz runs no web search today: see below. |

```python
model="qwen/qwen3.8-27b:floor"
```

llm.buzz runs no web search today. A request that asks for one, with `:online`, `web_search_options` or `plugins: [{"id": "web"}]`, is still served, without the search: nothing is sent to a search service, no search is billed, and `x-sgr-ignored-parameters` names it as `web_search->dropped(search_unavailable)`. For an organization whose requests llm.buzz keeps in the EU, such a request answers 400 `web_search_unavailable_in_region` instead.

## How a provider is chosen

Each model has a routing preset that orders its providers:

| Preset | Order |
|---|---|
| `smart` | By price and time to first token together. On a model with the llm.buzz provider, llm.buzz goes first and the other providers are the failover. The default: every new model starts on it, and a model on `order` takes it when its first independent provider goes live. |
| `cheapest` | By the estimated cost of the request at each provider's price. |
| `fastest` | By recent time to first token. |
| `order` | In the order llm.buzz set. |

The request goes to the first provider in that order. If it errors or times out, the next provider for the same model answers.

`provider.sort` overrides the preset for one request: `"price"` works as `:floor`, `"latency"` and `"throughput"` as `:nitro`, and `"smart"` ranks by price and speed.

```python
extra_body={"provider": {"sort": "price"}}
```

## Which providers serve

A model serves one API: every provider of it serves every capability the model declares. Before a provider serves a model, an automated check sends it real requests for each one: a streamed answer with usage, then tool calls, structured output and image input where the model declares them. A provider that fails stays out of routing.

Each provider's prices follow the prices it publishes, such as DeepInfra's, Novita's or Scaleway's price lists, and sync about every six hours. Where a provider publishes no price list, its prices come from its public listed prices, or llm.buzz sets them by hand; a price set by hand is what you pay and what the model pages show. Scaleway lists its prices in euros, converted to US dollars at the European Central Bank's daily rate plus 3%, the cost of converting. Every price you see or pay is in US dollars.

## Discounts

A provider may run a discount for a set time. While it runs, the model page shows a shield and "N% off" next to that provider, with each price struck through beside the price you pay, and every request that provider serves is billed at the discounted price, including requests that started before the discount ended. `smart` and `cheapest` routing compare the discounted prices. `GET https://api.llm.buzz/api/v1/models/{author}/{slug}/endpoints` lists the discounted prices with `discount`, the fraction off (`0.51` for 51% off), as OpenRouter does, and `0` when none runs. The price history on a model page shows prices before discounts.

## Provider preferences

The `provider` object narrows or reorders the providers of one request, as on OpenRouter. It works on Chat Completions, Responses, Messages, Embeddings and the image routes. On the audio routes only `zdr` and `data_collection` apply; see [Audio](audio.md).

| Field | Effect |
|---|---|
| `order` | Try these providers first, in this order, then the others in the preset's order. |
| `only` | Use only these providers. |
| `ignore` | Never use these providers. |
| `allow_fallbacks` | `false` stops moving to other providers: without `order` only the first provider is tried, and with `order` only the listed ones. |

A name matches a provider's display name, its tag or the tag's first part, ignoring case. Tags come from the endpoints route; an EU provider's tag ends in `/eu`, such as `scaleway/eu`. With an `order`, sort suffixes and `provider.sort` no longer apply.

```python
extra_body={"provider": {"order": ["deepinfra"], "allow_fallbacks": False}}
```

When your preferences leave the model no provider, the answer is 404 `no_allowed_providers`, and its message names the providers that serve the model. Nothing is sent to a provider.

How a request moves to the next provider, and what a failed attempt or a retry of your own costs, is on [Retries and idempotency](retries-and-idempotency.md).

## The llm.buzz provider

Many models list a provider named llm.buzz beside the companies that serve them directly. It is llm.buzz's own route to the model, and it takes part in routing and failover like any other provider.

- **Its price.** llm.buzz has no price list of its own. For each price (input, cached input, cache writes, output and reasoning) it bills the lowest price among the model's live direct providers, their running discounts applied, and any discount llm.buzz runs on top. A request it serves never costs more than the cheapest direct provider of each price. The model pages show its prices with an Auto tag, and the price history shows only the direct providers.
- **Its place in the order.** Under `smart` it goes first while it is healthy, and the model's direct providers are the failover. Under `cheapest`, `fastest` and `order`, and in your own `provider.order`, it ranks like any provider.
- **Its names.** `llm.buzz` or `llm-buzz` in `provider.order`, `only` or `ignore` names it, and its tag on the endpoints route is `llm-buzz`. `{"ignore": ["llm.buzz"]}` keeps a request on the direct providers. `llm-buzz/eu` names only its capacity in the EU; when it has none for the model, the request answers 404 `no_allowed_providers`, saying so.
- **Its answers.** The answer's `provider` field and its `x-gateway-provider` header say `llm.buzz`, and `usage` carries the tokens billed at its price.
- **Its data policy.** Its declared data policy holds for every request it serves, and `provider.zdr` and `provider.data_collection` judge it by that declaration, as they judge any provider.

## EU

Each provider serves from the EU or from outside it, and an EU provider's tag ends in `/eu`. To keep a request on EU providers, list their EU tags in `provider.only`:

```python
extra_body={"provider": {"only": ["scaleway/eu", "simpleminds-eu/eu", "surogate-spark/eu"]}}
```

A request that continues an earlier one (a Responses `previous_response_id`, or a reasoning continuation) can stay on the provider that served the first, whatever `only` says.

For an organization whose requests llm.buzz keeps in the EU, a model with no EU provider answers 404 `model_not_available_in_region`.

## Data policy

Each provider declares whether it trains on prompts and how many days it keeps them. Two request fields keep a request away from every provider whose declaration does not qualify:

| Field | Providers that qualify |
|---|---|
| `provider.zdr: true` | Declare that they keep prompts for 0 days. |
| `provider.data_collection: "deny"` | Declare that they do not train on prompts and keep them for 0 days. |

```python
extra_body={"provider": {"data_collection": "deny"}}
```

Your organization can require the same of every request: on the console's Prompt logs page, set its data policy to no training and no retention. It applies from the next request, and a request can tighten it but never loosen it.

A provider that has not declared a policy never qualifies. When no provider of the model qualifies, the answer is 404 `model_not_available_under_data_policy`, and nothing is sent to a provider.

For OpenRouter's privacy settings, see [Coming from OpenRouter](from-openrouter.md#prompt-logs).

## Prompt logs

llm.buzz keeps the prompts and answers of your organization's completed Chat Completions, Responses and Messages requests in prompt logs, and logging is on by default; Embeddings, Images and the Responses WebSocket are not logged. Your organization's admins read the logged prompts, turn logging off, and delete every logged prompt on the console's Prompt logs page, the page that also sets the organization's data policy.

- **What is never logged.** A request with `provider.zdr: true`, with `provider.data_collection: "deny"`, or sent while your organization's data policy is no training and no retention is never logged, whatever the logging setting says.
- **How long.** Logged prompts are kept for 30 days by default; llm.buzz sets one window for every organization, and the Prompt logs page shows the number of days in force.
- **Turning logging off** applies from the next request. It removes nothing already logged: a logged prompt stays until its days pass or an admin deletes it.
- **Deleting** removes every logged prompt of your organization, whichever member's request it was. They disappear from the page at once and are removed within about a minute. Requests sent a few seconds after the delete are logged again while logging is on.
- **What stays.** Usage, cost and the request log stay when you turn logging off or delete.
- **Not prompt logs.** llm.buzz holds a Chat Completions or Responses request sent with an `Idempotency-Key` for up to 24 hours, so a retry can return its answer, and a Responses turn for up to 24 hours, so `previous_response_id` can continue it. The console does not show either, and neither follows the logging setting or a zero-retention demand. A Responses request with `store: false` keeps its turn out.
