# Provider Routing (/docs/provider-routing)

Many models are served by more than one provider. `zhipu/glm-5.3-flash`, for example:

```bash
curl -s https://api.ohmygpt.com/v1/models/zhipu/glm-5.3-flash | jq -r '.providers[].provider'
```

```text
chatglm
tencent
```

You don't have to choose: by default OhMyGPT picks a provider for each request by weighing price against speed and reliability, and keeps a conversation with the provider that already holds its prompt cache. If that provider fails before answering, the request goes to the next one. When you do want a say, for example the cheapest provider or a fixed fallback order, add a top-level `provider` object on `/v1/chat/completions` or `/v1/responses`:

```bash
curl https://api.ohmygpt.com/v1/chat/completions \
  -H "Authorization: Bearer $OHMYGPT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "zhipu/glm-5.3-flash",
    "messages": [{"role": "user", "content": "Hi"}],
    "provider": {"order": ["chatglm"]}
  }'
```

## Fields [#fields]

| Field | Type | Effect |
|---|---|---|
| `order` | string[] | Try these providers first, in this order. The model's other providers stay available as fallbacks. |
| `only` | string[] | Use only these providers. |
| `ignore` | string[] | Never use these providers. |
| `allow_fallbacks` | boolean | Defaults to `true`. With `false`, only the providers in `order` are used; without `order`, only the first-ranked provider is used. |
| `sort` | string or object | Prefer providers by `price` (lowest cost for this request), `latency` (fastest time to first token) or `throughput` (most tokens per second). The object form `{"by": "price"}` works too. |

Use the slugs from the model's `providers[].provider` list; case doesn't matter. OpenRouter's provider slugs work too: `amazon-bedrock`, `google-vertex`, `google-ai-studio`, `claude-on-aws`, `moonshotai`, `z-ai` and `seed` mean `bedrock`, `vertex`, `google`, `anthropic_aws`, `moonshot`, `chatglm` and `bytedance`. Keep at least one of the model's providers in `only`, and don't `ignore` all of them: a request that leaves no provider is rejected with `400 invalid_parameter`, naming `provider.only` or `provider.ignore` (or `provider.order` when `allow_fallbacks` is `false`). With both `order` and `sort`, the providers in `order` come first and `sort` ranks the rest.

To see which provider served a request, [look it up](/docs/costs#look-up-a-request) by its `X-Request-ID`. The receipt's `model.provider` field names it.

## From the OpenAI SDKs [#from-the-openai-sdks]

Pass `provider` as an extra body field:

```python tab="Python"
completion = client.chat.completions.create(
    model="zhipu/glm-5.3-flash",
    messages=[{"role": "user", "content": "Hi"}],
    extra_body={"provider": {"order": ["chatglm"]}},
)
```

```ts tab="TypeScript"
const completion = await client.chat.completions.create({
  model: 'zhipu/glm-5.3-flash',
  messages: [{ role: 'user', content: 'Hi' }],
  // @ts-expect-error provider is an OhMyGPT field, not in the OpenAI types
  provider: { only: ['chatglm'] },
})
```

## Pin a provider in the model ID [#pin-a-provider-in-the-model-id]

Prefix the model ID with a provider slug: `tencent:zhipu/glm-5.3-flash` sends the request to Tencent only, like `"only": ["tencent"]`. The prefix works on every endpoint. On `/v1/messages` and the Gemini endpoint it is the way to choose a provider; leave the `provider` object out there.

## Models routed by OhMyGPT [#models-routed-by-ohmygpt]

For models with `managed_routing: true` in the [model list](/docs/model-naming), OhMyGPT picks the provider for you. Call them by their plain ID, without a prefix or a `provider` object; if you send one, its choices are ignored. The charge on each response reflects the provider that served it.

## Migrating from OpenRouter [#migrating-from-openrouter]

| OpenRouter | OhMyGPT |
|---|---|
| `model: "x:floor"` | `"provider": {"sort": "price"}` |
| `model: "x:nitro"` | `"provider": {"sort": "throughput"}` |
| `"provider": {"order": [...]}` | Same field, with OpenRouter's slugs |
| `"provider": {"only": [...]}`, `"ignore": [...]` | Same fields, with OpenRouter's slugs |
| `"provider": {"allow_fallbacks": false}` | Same field |
| `"provider": {"sort": {"by": "price"}}` | Same field |
| `models: [...]` fallback list | Pick one model; fallback across its providers is automatic |

Send model IDs exactly as listed, with no suffix. Leave out other `provider` fields such as `zdr`, `data_collection`, `max_price` and `quantizations`; requests that set them are rejected with `400 unsupported_parameter`.

An invalid value names the field to fix:

```json
{
  "error": {
    "param": "provider.sort",
    "code": "invalid_parameter",
    "message": "Invalid `provider.sort`: must be one of `price`, `latency`, `throughput`, not `speed`.",
    "type": "invalid_request_error"
  }
}
```
