# Gateway headers and extensions

Source: https://platform.lyrouter.com/ai_gateway/docs/gateway-headers/en
Last updated: 2026-10-19

Besides the standard OpenAI and Anthropic fields, LYRouter defines a few request headers, response headers and special model names: request headers control parameter validation, declare a session and require native passthrough; response headers report dropped parameters and remaining quota; special model names call a virtual model, Auto routing or the Default Model. All of these are optional, and calls without them work as in the standard protocols. HTTP header names are case-insensitive.

## Quick reference

| Name | Direction | Purpose | Endpoints |
| --- | --- | --- | --- |
| `X-Gateway-Strict-Parameters: true` | Request | Returns 400 instead of dropping parameters | Images, videos |
| `x-session-id` | Request | Declares a session so logs are grouped by it; forwarded to the upstream as is | All |
| `x-gateway-passthrough: require` | Request | Requires Anthropic native passthrough and returns 400 when it is not possible | `/v1/messages` |
| `X-Title` | Request | Identifies your application in the Client column of the logs | All |
| `X-Gateway-Unsupported-Parameters` | Response | Lists the parameters the gateway dropped | Images, videos |
| `x-aigw-quota-<metric>-limit` / `-remaining` / `-reset` | Response | Limit, remaining amount and reset time of the applicable quotas | Endpoints that call a model |
| `anthropic-ratelimit-*` | Response | Rate limit information in the Anthropic format | `/v1/messages` |
| `Retry-After`, `X-RateLimit-Reason`, `X-RateLimit-Reset` | Response | The API key is circuit-broken or banned | Endpoints that call a model |
| `@vmodel/<name>`, `@preset/<name>` | Model name | Calls a virtual model | Chat endpoints and others |
| `lyrouter/auto` | Model name | Auto routing | Chat endpoints |
| `model` omitted | Model name | Uses the workspace's Default Model | All model endpoints |

## Request headers

### X-Gateway-Strict-Parameters

Image and video endpoints drop parameters the selected model does not support, carry on, and list the dropped parameters in the `X-Gateway-Unsupported-Parameters` response header. With this request header, the gateway returns 400 as soon as any parameter would be dropped, without calling the upstream and without billing.

- **Value**: only lowercase `true` is recognized; any other value (including `True` and `1`) counts as absent.
- **Endpoints**: `/v1/images/generations`, `/v1/images/edits`, `/v1/videos`, `/v1/videos/regenerations`, `/v1/videos/context`. It does not control the parameters chat endpoints remove during cross-protocol conversion; see [Chat Completions](https://platform.lyrouter.com/ai_gateway/docs/chat-completions/en).

```bash
curl https://api.lyrouter.com/v1/images/generations \
  -H "Authorization: Bearer $LYROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Gateway-Strict-Parameters: true" \
  -d '{"model":"MODEL_ID","prompt":"A Shiba Inu wearing sunglasses","guidance_scale":7}'
```

When the selected model does not support `guidance_scale`, the response is:

```json
{
  "error": {
    "message": "unsupported parameters for this model: guidance_scale",
    "type": "invalid_request_error",
    "code": "unsupported_parameters"
  }
}
```

The parameters each model supports are listed in [Multimodal capabilities](https://platform.lyrouter.com/ai_gateway/docs/multimodal/en).

### x-session-id

Assign a fixed session ID to a conversation (for example, generate a UUID for each conversation) and send the same value with every request in that session. For log grouping, `x-session` is equivalent.

- **Log grouping**: the Session column of the [logs](https://platform.lyrouter.com/ai_gateway/docs/logs/en) groups requests by this value, with the source shown as "Declared (client x-session-id)", and you can also filter by Session ID. Without this header, the gateway infers sessions from the conversation content, the source shows "Inferred (gateway-derived)", and the tag ends with `≈`.
- **Forwarded as is**: the gateway forwards this header to the upstream without changing its value.
- **Upstream session affinity**: the platform has turned on session affinity for a few channels. On those channels, when you do not send this header, the gateway adds `X-Session-Id: aigw-<hash>` automatically; when you send it, your value is used. Whether it affects upstream caching or instance assignment is up to the provider.
- **Does not affect gateway routing**: the gateway's own session affinity works per "API key + model", as described in [Channels and routing](https://platform.lyrouter.com/ai_gateway/docs/routing/en), and has nothing to do with this header.

**Request examples**

cURL:
```bash
curl https://api.lyrouter.com/v1/chat/completions \
  -H "Authorization: Bearer $LYROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -H "x-session-id: 6f1c7e2a-3b4d-4e5f-9a8b-1c2d3e4f5a6b" \
  -d '{"model":"MODEL_ID","messages":[{"role":"user","content":"Let us pick up where we left off"}]}'
```

Python:
```python
import os
import uuid
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["LYROUTER_API_KEY"],
    base_url="https://api.lyrouter.com/v1",
)
session_id = str(uuid.uuid4())  # Generate once per conversation, then reuse it every turn
completion = client.chat.completions.create(
    model="MODEL_ID",
    messages=[{"role": "user", "content": "Let us pick up where we left off"}],
    extra_headers={"x-session-id": session_id},
)
```

Node.js:
```js
import { randomUUID } from "node:crypto";
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.LYROUTER_API_KEY,
  baseURL: "https://api.lyrouter.com/v1",
});
const sessionId = randomUUID(); // Generate once per conversation, then reuse it every turn
const completion = await client.chat.completions.create(
  {
    model: "MODEL_ID",
    messages: [{ role: "user", content: "Let us pick up where we left off" }],
  },
  { headers: { "x-session-id": sessionId } },
);
```

### x-gateway-passthrough

When you call an Anthropic-protocol channel through `/v1/messages`, the gateway by default converts the request into its internal format and back. Sending `x-gateway-passthrough: require` means "the request body must reach the upstream as is, with no conversion": when that cannot be met, the gateway returns 400 and states which condition failed, instead of silently falling back to conversion.

| Value | Behavior |
| --- | --- |
| `require` (case-insensitive) | Native passthrough is mandatory; returns 400 when it is not possible |
| `prefer`, any other value, or absent | Default behavior: passthrough when possible, conversion otherwise, and the request completes as usual |

- This header is read only by the gateway and is not forwarded to the upstream.
- The LYRouter public gateway currently has native passthrough turned off, so every request with `require` returns 400; do not send this header unless you need it.

When a request is rejected, the error message is ``native passthrough was required by `x-gateway-passthrough: require` but is not available for this request (<reason>)``. On `/v1/messages` it comes back in the Anthropic error format (`invalid_request_error`); on other endpoints (such as `/v1/responses`) it is a single line of plain text, yet its response header is currently still `Content-Type: application/json`, so check whether the body is JSON before parsing it. For what each reason code in parentheses means and how to handle it, see [Protocol compatibility and limits](https://platform.lyrouter.com/ai_gateway/docs/compatibility/en#native-passthrough).

### X-Title

Send `X-Title: <app name>` in the request headers. When the gateway has not recognized a known tool (such as Claude Code or Cursor) from other signals, the Client column of the [logs](https://platform.lyrouter.com/ai_gateway/docs/logs/en) shows this app name, and usage analytics uses it for the client breakdown. This header is forwarded to the upstream as is.

## Response headers

### X-Gateway-Unsupported-Parameters

When the gateway drops parameters from an image or video request, the response carries this header, with comma-separated parameter names as the value, for example `X-Gateway-Unsupported-Parameters: response_format` or `guidance_scale,watermark`. The request itself runs as usual. In production, check this header, or use `X-Gateway-Strict-Parameters: true` to turn dropping into an error.

### x-aigw-quota-*

When a [quota](https://platform.lyrouter.com/ai_gateway/docs/quota/en) applies to the call, the response carries quota headers. They appear on successful responses and on the 429 returned when a quota is reached, and also on many 400s, for example a wrong `Content-Type`, a model that does not support the endpoint called, a 400 returned by the upstream, or a rejected native passthrough; 401, 403, 503 and the 400 for a missing `model` (`missing_model`) do not carry them. There are three headers per applicable metric:

| Response header | Meaning |
| --- | --- |
| `x-aigw-quota-<metric>-limit` | The limit. Whole Credits for `credits`; token or request counts for the others |
| `x-aigw-quota-<metric>-remaining` | The remaining amount, based on usage when the request was sent and excluding this request's consumption; never below 0 |
| `x-aigw-quota-<metric>-reset` | The next reset time, as a UTC instant in RFC 3339 format. This value is currently later than the actual reset: about 2 minutes later for daily and monthly quotas (for example, a monthly quota that resets at `16:00:00Z` returns `16:02:00Z` to `16:02:06Z`) and about 1 minute later for per-minute quotas, and it also varies by a few seconds between requests. Use it only to estimate the reset time, not for precise timing. Total quotas without a reset period do not include this header |

The metrics are `credits`, `tokens`, `requests`, `input-tokens` and `output-tokens`. When several quotas apply to the same metric at once (for example an API key quota and a workspace quota), the one with the least remaining is reported.

```text
x-aigw-quota-credits-limit: 2000
x-aigw-quota-credits-remaining: 1532
x-aigw-quota-credits-reset: 2026-10-31T16:02:00Z
x-aigw-quota-requests-limit: 600
x-aigw-quota-requests-remaining: 597
x-aigw-quota-requests-reset: 2026-10-14T06:31:00Z
```

Reading response headers with an SDK:

**Reading response headers**

cURL:
```bash
curl -s -D - -o /dev/null https://api.lyrouter.com/v1/chat/completions \
  -H "Authorization: Bearer $LYROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"MODEL_ID","messages":[{"role":"user","content":"Hello"}]}' \
  | grep -i '^x-aigw-quota-'
```

Python:
```python
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["LYROUTER_API_KEY"],
    base_url="https://api.lyrouter.com/v1",
)
raw = client.chat.completions.with_raw_response.create(
    model="MODEL_ID",
    messages=[{"role": "user", "content": "Hello"}],
)
print(raw.headers.get("x-aigw-quota-credits-remaining"))
completion = raw.parse()
```

Node.js:
```js
import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.LYROUTER_API_KEY,
  baseURL: "https://api.lyrouter.com/v1",
});
const { data: completion, response } = await client.chat.completions
  .create({ model: "MODEL_ID", messages: [{ role: "user", content: "Hello" }] })
  .withResponse();
console.log(response.headers.get("x-aigw-quota-credits-remaining"));
```

Quota headers appear only on endpoints that call a model (chat, Responses, Messages, embeddings, rerank, images, audio and video submission); `/v1/models`, video task queries and similar endpoints do not carry them. For the 429 returned when a quota is used up, see [429: rate limits and quotas](https://platform.lyrouter.com/ai_gateway/docs/rate-errors/en).

### Retry-After, X-RateLimit-Reason, X-RateLimit-Reset

These headers appear only when the gateway circuit-breaks or bans an API key because of a high error rate:

| Scenario | Status | Response headers |
| --- | --- | --- |
| Temporary circuit break (`api_key_circuit_open`) | 429 | `Retry-After: <seconds>`, `X-RateLimit-Reason: api_key_circuit_open`, `X-RateLimit-Reset: <Unix seconds when it lifts>` |
| Permanent ban (`api_key_permanently_banned`) | 403 | `X-RateLimit-Reason: api_key_permanently_banned` |

- A 429 for a used-up quota carries no `Retry-After`; wait according to `reset_at` in the error body or `x-aigw-quota-*-reset`.
- The upstream provider's own `Retry-After` is forwarded to you only on `/v1/messages`.
- How to handle these is covered in [429: rate limits and quotas](https://platform.lyrouter.com/ai_gateway/docs/rate-errors/en) and [401 / 403: authentication and access](https://platform.lyrouter.com/ai_gateway/docs/auth-errors/en).

### anthropic-ratelimit-*

When you call through `/v1/messages`, the response carries rate limit headers in the Anthropic format, which clients such as Claude Code use to pace their requests:

- When the upstream serving the request returns `anthropic-ratelimit-*`, the gateway forwards them as is (together with the upstream's `Retry-After`); these numbers are the upstream account's limits.
- When the upstream returns none and you have request or token quotas, the gateway generates `-limit`, `-remaining` and `-reset` for `anthropic-ratelimit-requests-*`, `-tokens-*`, `-input-tokens-*` and `-output-tokens-*` from your quotas. Credits quotas have no Anthropic equivalent; read `x-aigw-quota-credits-*` instead.

It is normal for `anthropic-ratelimit-*` and `x-aigw-quota-*` to show different numbers in the same response: the former may come from the upstream, while the latter is always your quota on LYRouter.

### Request ID

The gateway does not return a request ID in the response headers. To investigate a particular call, copy the Request ID at the top of the log detail in the console; see [Errors and request identifiers](https://platform.lyrouter.com/ai_gateway/docs/errors/en).

The response body from some upstreams (such as channels serving GLM models) contains `"request_id":"aigw-…"`: this is the session ID the gateway adds automatically (`X-Session-Id`; see "x-session-id" above), echoed back by the upstream. It is not LYRouter's Request ID. When the request carries `x-session-id`, the echoed value is the one you sent.

## Special model names

| Value | Meaning | Available on |
| --- | --- | --- |
| `@vmodel/<name>` | Calls a [virtual model](https://platform.lyrouter.com/ai_gateway/docs/presets/en) in the workspace | `/v1/chat/completions`, `/v1/messages`, `/v1/responses`; when the virtual model's model list consists entirely of embedding or rerank models, also `/v1/embeddings` and `/v1/rerank` |
| `@preset/<name>` | The old prefix for virtual models, equivalent to `@vmodel/` | Same as above |
| `lyrouter/auto` | [Auto routing](https://platform.lyrouter.com/ai_gateway/docs/auto/en), which picks among the candidate models by the intent of the request | `/v1/chat/completions`, `/v1/messages`, `/v1/responses` |
| `model` omitted, or `""` or `null` | Uses the workspace's [Default Model](https://platform.lyrouter.com/ai_gateway/docs/routing/en) | Every endpoint that takes `model` |

- Virtual models and `lyrouter/auto` used on image, video or audio endpoints return 403 `Model is not available in the model plaza`; these endpoints accept only specific model IDs.
- Enabled virtual models and `lyrouter/auto` appear in the results of `GET /v1/models`; see [List models](https://platform.lyrouter.com/ai_gateway/docs/models/en).
- When `model` is omitted and the workspace has no Default Model, the request returns 400 `you must provide a model parameter` (`missing_model`). The Default Model is usually a chat model, so omitting `model` on embeddings, rerank or audio endpoints most likely returns 400 `unsupported_model_api`; always name the model ID on these endpoints. How to write model IDs is covered in [Model identifiers and discovery](https://platform.lyrouter.com/ai_gateway/docs/model-names/en).
