Move from Portkey

Portkey keeps its routing rules in a config that a request names in the x-portkey-config header. The exchange has no config object and reads no x-portkey-* header. It takes its rules in an exchange object in the request body, or from a preset. The move is more than a new base URL.

Change these four things

  1. Issue a spend key on the API keys page. See API keys.
  2. Use a client that sends the OpenAI format. Set its base URL to https://router.inference.ai.exchange/v1, and send the key as Authorization: Bearer sk-liquid-…. See OpenAI SDK.
  3. Remove the x-portkey-* headers. The x-liquid-job-id response header gives the id of each job.
  4. Change each model to a name that GET /v1/models lists. See Model names.

Then:

  1. Move each config to an exchange object or a preset. See Settings that change and Portkey features.
  2. Run a dry run. See Check a request without a purchase.
  3. Read What is different about money. The exchange never lowers an output limit that you set. See Output limits.

What differs

Example

A Portkey config with a fallback, a retry and a cache:

{
  "strategy": { "mode": "fallback", "on_status_codes": [429, 503] },
  "targets": [
    { "override_params": { "model": "@openai-prod/gpt-4o" } },
    { "override_params": { "model": "@anthropic-prod/claude-sonnet-4-6" } }
  ],
  "retry": { "attempts": 3 },
  "cache": { "mode": "simple", "max_age": 600 }
}

The same rules for the exchange. The basket replaces the targets: the two models compete, and the lowest estimated charge wins. The cache headers replace cache. One failure returns one error. The model names are illustrations.

curl https://router.inference.ai.exchange/v1/chat/completions \
  -H "Authorization: Bearer $LIQUID_API_KEY" \
  -H "x-liquid-cache: true" \
  -H "x-liquid-cache-ttl: 600" \
  -H "x-liquid-tag: support" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "architect.basket",
    "messages": [{"role": "user", "content": "Say hello."}],
    "exchange": {
      "candidates": { "models": { "allow": ["gpt-oss-120b", "gpt-oss-20b"] } },
      "rank": { "strategy": { "kind": "price" } },
      "cap_usd": 0.05
    }
  }'

To save these rules, make a preset on the Presets page and send its name as model.

Reference

Settings that change

Portkey setting Exchange setting
x-portkey-provider, provider, x-portkey-virtual-key and virtual_key No equal. Use exchange.candidates.providers.allow or exchange.candidates.providers.exclude with provider handles. A Portkey slug is not a handle.
x-portkey-config and config An exchange object in the body, or a preset that you name as model. See Purchasing rules and Presets.
x-portkey-metadata x-liquid-tag with one label. See Cost tags.

Model names

The exchange does not use the @provider-slug/model-name form. Send a name that GET /v1/models returns, a router model, a basket or a preset. See Connect an app.

Portkey features

Portkey feature What the exchange does
strategy.mode fallback, targets, on_status_codes Not read. Send architect.basket with exchange.candidates.models.allow, and rank with exchange.rank.strategy. See Errors.
strategy.mode loadbalance, weight, conditional Not read or not available. The exchange awards each job to one offer by rank. Choose the model in your own code. See Purchasing rules.
retry with attempts Not read. If your code retries, send the same idempotency-key on each try, so that no second job runs. See Purchasing rules.
x-portkey-request-timeout Not read. exchange.limits.ttft_ms and exchange.limits.completion_ms limit which offers compete. A job that misses the deadline is still charged, and its receipt shows missed. See Purchasing rules.
cache with mode simple Send x-liquid-cache: true, or set exchange.cache.response in the rules. An identical request from the same key gets the stored answer. The entry lives 1 to 86,400 seconds. See Response caching.
cache with mode semantic Not available. Only an identical request matches.
x-portkey-cache-force-refresh, x-portkey-cache-namespace Not read. x-liquid-cache-clear: true deletes the entry and runs the request again. A key never shares an entry with another key.
input_guardrails, output_guardrails, before_request_hooks Not read. The workspace guardrails apply to every call. There is no output check. See Guardrails.
override_params, default_params, drop_params of a target Not available. Put the model and the parameters in the request.
tools, response_format, stream Applied. See Other OpenRouter fields.

Troubleshooting

For 402 insufficient_balance and 400 no_eligible_offer with param set to exchange.cap_usd, see What is different about money.

Error Cause Fix
404 model_not_found The model has a @provider-slug/ prefix, or is not on the exchange. Pick a name from GET /v1/models.
400 invalid_request, message names a field of exchange The exchange object has a Portkey key or an unknown field. Use only the fields in Purchasing rules.
no_eligible_offer, binding_rule is allowed_providers No provider in the allowlist quotes the model. A Portkey slug causes this. Use provider handles. See Market data.
400 unsupported_parameter, param is a parameter you set No offer for the model supports that parameter. Remove the parameter, or select another model. See Errors.
402 insufficient_balance The cheapest eligible cap is more than the balance. Add funds, or send a lower output limit.