# Preferred providers

Use `exchange.candidates.providers.prefer` to try providers in a specified order, then use the rest of the eligible market.
A preference changes the order of eligible offers. It does not exclude other providers.

Use `exchange.candidates.providers.allow` when only the specified providers can serve the request.
If you want fallback to other providers, replace that allowlist with a preference.

## Send a request

1. Choose a model from `GET /v1/models`.
2. Get provider handles from the model's page on the exchange.
3. Replace the example model and provider handles with your choices.
4. Send the request with a `spend` key.

See [API keys](https://inference.ai.exchange/docs/keys.md) if you need a key.

```bash
curl https://router.inference.ai.exchange/v1/chat/completions \
  -H "Authorization: Bearer $LIQUID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-oss-120b",
    "messages": [{ "role": "user", "content": "Say hello." }],
    "max_tokens": 64,
    "exchange": {
      "candidates": { "providers": { "prefer": ["acme", "globex"] } },
      "cap_usd": 0.05
    }
  }'
```

With the OpenAI Python SDK, put `exchange` in `extra_body`:

```python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://router.inference.ai.exchange/v1",
    api_key=os.environ["LIQUID_API_KEY"],
)

response = client.chat.completions.create(
    model="gpt-oss-120b",
    messages=[{"role": "user", "content": "Say hello."}],
    max_tokens=64,
    extra_body={
        "exchange": {
            "candidates": { "providers": { "prefer": ["acme", "globex"] } },
            "cap_usd": 0.05,
        }
    },
)
print(response.choices[0].message.content)
```

## How the exchange orders offers

For the example above, an eligible offer from `acme` ranks before an eligible offer from `globex`.
Other eligible offers follow. If neither preferred provider has an eligible offer, the rest of the market can serve the request.

- Each entry is a provider handle or a pattern with `*`, such as `acme-*`.
- The first entry that matches a provider sets its preferred position. Put specific handles before broader patterns.
- Offers at the same preferred position use the request's usual order, such as price or speed.
- A [reservation](https://inference.ai.exchange/docs/reservations.md) leg ranks before a preferred provider.

A preferred provider can cost more than another provider. Keep a price cap and any required limits in the request.
Every offer must pass the request's limits, the preset of the key, and the guardrails of the workspace.
See [Purchasing rules](https://inference.ai.exchange/docs/rules.md).

## Fallback before output

If an award fails before any output, the exchange can try the next eligible offer.
The next offer can come from another preferred provider or from the rest of the market.
After output starts, the exchange does not switch providers for that response.

An allowlist still limits fallback. For example, this request permits only `acme` and `globex`, with `globex` first:

```json
{
  "exchange": {
    "candidates": {
      "providers": { "allow": ["acme", "globex"], "prefer": ["globex"] }
    }
  }
}
```

To permit fallback across the eligible market, remove the request's `exchange.candidates.providers.allow` limit.
The limits of the key and the workspace still apply.

## OpenRouter requests

The exchange maps `provider.order` to `exchange.candidates.providers.prefer` on Chat Completions, Messages and Responses requests.
This object asks for the same provider order as the first example:

```json
{
  "provider": {
    "order": ["acme", "globex"],
    "allow_fallbacks": true
  }
}
```

Use provider handles from the exchange. Do not assume that an OpenRouter provider name is the same handle.

With `allow_fallbacks: false`, only providers in `order` can compete.
The exchange can still try the next eligible provider in that list before any output.
If `provider.only` or `exchange.candidates.providers.allow` is also present, an offer must pass all these lists.

Send either `provider.order` or `exchange.candidates.providers.prefer`.
If you send both non-empty lists, they must contain the same entries in the same order.
Different lists return HTTP `400`.

The exchange refuses `allow_fallbacks: false` without a non-empty `order`.
See [Move from OpenRouter](https://inference.ai.exchange/docs/move-from-openrouter.md) for the full migration guide.

## Presets and Auto

A named [preset](https://inference.ai.exchange/docs/rules.md#presets) can save preferred providers with a price or speed strategy.
The request's list replaces the saved list. An empty request list clears the saved preference:

```json
{ "exchange": { "candidates": { "providers": { "prefer": [] } } } }
```

A guardrail cannot hold provider preferences. A preset linked to an API key does not apply its preferences.
To apply saved preferences, send the preset's name as `model`.

[Auto](https://inference.ai.exchange/docs/auto.md) and `smartest` rank by score.
They refuse a non-empty preference list with `400 preferred_providers_with_auto`.
This rule also applies to `provider.order` and preset saves.
Use a named model or a basket with a price or speed strategy.

## Check the order without a purchase

`POST /v1/exchange/dry-run` shows eligible offers in award order. It buys nothing and holds no funds.
Send preferences in `exchange`, because this endpoint does not read `provider`:

```bash
curl https://router.inference.ai.exchange/v1/exchange/dry-run \
  -H "Authorization: Bearer $LIQUID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-oss-120b",
    "exchange": {
      "candidates": { "providers": { "prefer": ["acme", "globex"] } },
      "cap_usd": 0.05
    }
  }'
```

If a provider limit excludes every offer, inspect `error.binding_rule` in the error response.
See [Auction errors](https://inference.ai.exchange/docs/errors.md#auction) for the cause and the required change.
