Move from OpenRouter

The exchange serves the OpenAI Chat Completions format, so most OpenRouter code changes in two places: the base URL and the API key. The exchange is an auction. It does not send your request to a fixed provider, so read What is different about money before you send traffic.

Change these two things

  1. Issue a spend key on the API keys page. See API keys.
  2. Set the base URL to https://router.inference.ai.exchange/v1. Send the key as Authorization: Bearer sk-liquid-….
from openai import OpenAI

client = OpenAI(
    base_url="https://router.inference.ai.exchange/v1",
    api_key="sk-liquid-...",
)

Then check three more things:

What differs

What is different about money

The exchange fixes a price cap before the first token. The cap is the most the job can cost. The exchange holds that amount from your workspace balance, charges the tokens used, and returns the rest. See Quickstart.

Output limits has the exact rule.

Check a request without a purchase

POST /v1/exchange/dry-run shows which offers would compete, in award order, and why the others are left out. It places no job and holds no funds. It takes model and an exchange object, and it does not read provider.

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": { "allow": ["acme"] } }, "cap_usd": 0.05 }
  }'

Example

An OpenRouter request:

{
  "model": "openai/gpt-oss-120b",
  "messages": [{ "role": "user", "content": "Say hello." }],
  "provider": {
    "only": ["acme"],
    "ignore": ["slow-*"],
    "max_price": { "prompt": 1, "completion": 2 },
    "data_collection": "deny",
    "sort": "price"
  }
}

The same request for the exchange. The model name is an illustration:

{
  "model": "gpt-oss-120b",
  "messages": [{ "role": "user", "content": "Say hello." }],
  "exchange": {
    "candidates": {
      "providers": { "allow": ["acme"], "exclude": ["slow-*"] }
    },
    "limits": { "price": { "input_usd_per_mtok": 1, "output_usd_per_mtok": 2 } },
    "privacy": { "retention": "none" },
    "rank": { "strategy": { "kind": "price" } },
    "cap_usd": 0.05
  }
}

Reference

Model names

The model field accepts a model, a router model, a basket or a preset. Connect an app has the table. An unknown name returns 404 model_not_found with suggestions. The exchange never uses a different model.

Routing settings

The exchange reads a provider object and applies each setting that it can apply exactly. Purchasing rules has the only table of every setting. In short:

A refused setting can have an exchange field to use instead:

Refused provider setting Use instead
preferred_min_throughput exchange.limits.min_tps (a limit, not a preference)
preferred_max_latency exchange.limits.ttft_ms or exchange.limits.completion_ms (limits)
quantizations, max_price.request, max_price.image, allow_fallbacks: false without order, any other sort, unknown settings No equal. Remove the setting, or rank with exchange.rank.strategy.

Other OpenRouter fields

OpenRouter feature What the exchange does
models (fallback list) Ignores it. Use architect.basket with exchange.candidates.models.allow. See Purchasing rules.
route, transforms, plugins, usage (request field) Ignores them. The response has the OpenAI usage object, and the exchange block has the cap and the charge.
Model suffix :online Use :web (beta). The exchange searches the web for the last user message and puts the results in the prompt. See Axis routing and Quickstart.
Model suffixes :nitro, :floor Part of the model name, so 404 model_not_found. Use :fastest or :cheapest. See Axis routing.
HTTP-Referer and X-Title headers Labels for your receipts. They never change a price, a route or an authorization.
reasoning Reads reasoning.effort, reasoning.max_tokens, reasoning.exclude and reasoning_effort. Matches offers that support the setting, or refuses with unsupported_parameter. exclude hides the reasoning, and the exchange still bills it.
response_format, tools, tool_choice, cache_control Applied. Only an offer that declares support can win. See Structured output and Prompt caching.
top_p, top_k, min_p, repetition_penalty, stop, seed, logprobs, top_logprobs, logit_bias, frequency_penalty, presence_penalty Passes them to a provider that declares support. If no offer supports one, unsupported_parameter.
n greater than 1 Refuses it.

Troubleshooting

Error Cause Fix
400 unsupported_parameter, param: "provider" A provider setting cannot map, or a price is negative or not a number. Read the message. See Routing settings. For provider.ignore, use a provider handle. See Market data.
400 unsupported_parameter, param is another field No offer for the model supports that parameter. Remove the parameter, or select another model.
404 model_not_found The model is not on the exchange. Pick a name from GET /v1/models.
no_eligible_offer The limits of the request, key or workspace excluded each offer. Read eligibility.excluded. Raise cap_usd, or relax a limit. Do not retry the same request. See Errors.
402 insufficient_balance The cheapest eligible cap is more than the balance. Add funds, or send a lower output limit.