Provider Routing
By default, the Router distributes requests across healthy providers using round-robin with automatic failover. X-0G-Provider-* request headers let you override this when you need specific behavior.
Default Behavior
If you send no routing headers, the Router:
- Picks a healthy provider for the requested model
- Retries on the next healthy provider if the first returns an error
- Returns the response — or a
503if every provider failed
This is the recommended path for most applications.
Routing surfaces
The Router accepts routing preferences from two surfaces. In priority order:
| Priority | Surface | Endpoints | Status |
|---|---|---|---|
| 1 | X-0G-Provider-* request headers | All inference endpoints (JSON, multipart, async) | Canonical |
| 2 | JSON body provider: {…} object | JSON endpoints only (/v1/chat/completions, /v1/messages, /v1/images/generations, /v1/async/images/generations) | Deprecated — kept for back-compat |
Headers and body are merged field-by-field; when the same field is set on both, the header wins. Multipart endpoints (/v1/audio/transcriptions, /v1/images/edits, /v1/async/images/edits) have no body routing surface — headers are the only way to control routing there.
provider object is deprecatedNew code should use X-0G-Provider-* headers. The body surface still works today for back-compat but will be phased out in a future release. Headers are the only routing surface that works uniformly across JSON, multipart, and async endpoints.
Routing Strategies
- Lowest Latency
- Lowest Price
- Cap Price
- Pin a Specific Provider
- Multipart (audio / image edit)
- JSON body (deprecated)
curl https://router-api.0g.ai/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-YOUR_API_KEY" \
-H "X-0G-Provider-Sort: latency" \
-d '{
"model": "zai-org/GLM-5-FP8",
"messages": [{"role": "user", "content": "Hello"}]
}'
Routes to the provider with the lowest recently-observed latency for this model.
curl https://router-api.0g.ai/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-YOUR_API_KEY" \
-H "X-0G-Provider-Sort: price" \
-d '{
"model": "zai-org/GLM-5-FP8",
"messages": [{"role": "user", "content": "Hello"}]
}'
Routes to the cheapest provider currently serving this model.
curl https://router-api.0g.ai/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-YOUR_API_KEY" \
-H "X-0G-Provider-Max-Price-Usd-Prompt: 1.0" \
-H "X-0G-Provider-Max-Price-Usd-Completion: 5.0" \
-d '{
"model": "zai-org/GLM-5-FP8",
"messages": [{"role": "user", "content": "Hello"}]
}'
Drops every provider above the ceiling before sorting and failover, so even a fallback during an outage can't route you to a more expensive provider. See Capping price per request.
curl https://router-api.0g.ai/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-YOUR_API_KEY" \
-H "X-0G-Provider-Address: 0xd9966e..." \
-d '{
"model": "zai-org/GLM-5-FP8",
"messages": [{"role": "user", "content": "Hello"}]
}'
Routes directly to a specific provider by on-chain address. Fallback is disabled by default when pinning — if the pinned provider fails, the request fails. Add X-0G-Provider-Allow-Fallbacks: true to re-enable cross-provider retry.
Multipart endpoints accept the same headers — this is the only routing surface available there.
curl https://router-api.0g.ai/v1/audio/transcriptions \
-H "Authorization: Bearer sk-YOUR_API_KEY" \
-H "X-0G-Provider-Sort: latency" \
-F "file=@recording.mp3" \
-F "model=openai/whisper-large-v3"
The legacy JSON body surface still works on JSON endpoints. New code should prefer headers.
{
"model": "zai-org/GLM-5-FP8",
"messages": [{"role": "user", "content": "Hello"}],
"provider": {
"sort": "latency"
}
}
When both surfaces are present and set the same field, the header wins.
Header Reference
HTTP header names are case-insensitive per RFC 7230 — X-0G-Provider-Address and x-0g-provider-address are equivalent.
| Header | Values | Description |
|---|---|---|
X-0G-Provider-Address | on-chain address (0x…) | Pin the request to a specific provider. Implies Allow-Fallbacks: false unless overridden. |
X-0G-Provider-Sort | latency | price | Sort strategy when no address is pinned. Ignored if X-0G-Provider-Address is set. Must be exactly latency or price — any other non-empty value is rejected with 400 invalid_provider_header. |
X-0G-Provider-Trust-Mode | standard | verified | private | Restrict provider selection to a trust tier — see Trust modes. |
X-0G-Provider-Allow-Fallbacks | true | false | Allow cross-provider retry on failure. Must be exactly true or false (case-insensitive) — 1, 0, yes, and other non-empty values are rejected with 400 invalid_provider_header. |
X-0G-Provider-Max-Price-Usd-Prompt | finite, non-negative decimal | Per-request ceiling on prompt token price, USD per 1M tokens. See Capping price per request. |
X-0G-Provider-Max-Price-Usd-Completion | finite, non-negative decimal | Per-request ceiling on completion token price, USD per 1M tokens. See Capping price per request. |
X-0G-Provider-Max-Price-Usd-Image | finite, non-negative decimal | Per-request ceiling on image price, USD per generated image. See Capping price per request. |
Defaults: Allow-Fallbacks is true normally, and false when X-0G-Provider-Address is set.
A header that is absent, or blank after trimming whitespace, is treated as unset and falls back to the default. Only a present-but-malformed value is rejected — a blank header meaning "I didn't set this" is never an error.
Trust modes
X-0G-Provider-Trust-Mode restricts selection by the provider's verification mode. The tiers are ordered standard < verified < private and act as a floor: asking for verified is also satisfied by the stronger private.
| Value | Routes to | Guarantee |
|---|---|---|
standard | Any TEE-backed provider | TEE-backed execution; the upstream discloses no independent verifiability method. |
verified | TeeML and TeeTLS providers | Verifiable execution — the response provably came from the real model. |
private | TeeML providers only | Verifiability and privacy — the model itself runs inside the TEE, so prompts never leave the enclave. |
Values other than standard/verified/private are rejected with 400 invalid_trust_mode. Omit the header for no trust-tier restriction (the default).
Capping price per request
The X-0G-Provider-Max-Price-Usd-* headers set a hard ceiling on what you're willing to pay. Any provider above the ceiling on a relevant dimension is dropped from the candidate pool entirely — this is a filter, not a preference, and it runs before sorting and failover. A fallback during an outage can never silently route you to a provider you've priced out.
curl https://router-api.0g.ai/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-YOUR_API_KEY" \
-H "X-0G-Provider-Max-Price-Usd-Prompt: 1.0" \
-H "X-0G-Provider-Max-Price-Usd-Completion: 5.0" \
-d '{
"model": "zai-org/GLM-5-FP8",
"messages": [{"role": "user", "content": "Hello"}]
}'
Send any subset — one header, two, or all three. Each value is a finite, non-negative decimal; NaN, Inf, negative, and non-numeric values are rejected with 400 invalid_max_price_usd.
Which dimension applies to which endpoint
The ceiling is service-type aware. Setting Image on a chat call (or Prompt / Completion on an image call) is silently inert, so a cross-endpoint SDK that always sends all three headers won't accidentally filter out every provider.
| Service type | Endpoints | Dimensions enforced | Unit |
|---|---|---|---|
| Chat | /v1/chat/completions, /v1/messages | Prompt, Completion | USD per 1M tokens |
| Image | /v1/images/generations, /v1/images/edits, /v1/async/images/* | Image | USD per generated image |
| Speech-to-text | /v1/audio/transcriptions | none yet — see below | — |
STT models are billed per second of audio, which has no equivalent in the current USD pricing schema (prompt / completion / image only). Reusing the Prompt header for STT would be a footgun — the same 1.0 would mean "1 per second" on audio — so /v1/audio/transcriptions enforces no ceiling for now.
Two failure modes are worth calling out:
- No provider qualifies. If the ceiling filters out every candidate, the request fails with
400 no_provider_within_max_price, not503— the pool is empty structurally, not transiently, so retrying without raising the ceiling won't help. - Pinning + ceiling. If
X-0G-Provider-Addresspins a provider above the ceiling, the request fails with400 pinned_provider_exceeds_max_price— the pin isn't silently overridden.
Discovering Provider Addresses
List the providers serving a model with GET /v1/providers?model_id=… — see Models.
Related
- Principles — why failover is the default
- Errors — what
502and503mean for routing