---
title: "Request and response headers"
description: "The request headers the pirouter gateway reads and the response headers it writes. This page lists what each header means, which responses carry it, and which request headers a browser may send."
canonical: "https://pirouter.ai/docs/headers"
last-updated: "2026-10-17"
lang: "en"
source: "content/docs/en/(start)/headers.mdx"
---

# Request and response headers

The request headers the pirouter gateway reads and the response headers it writes. This page lists what each header means, which responses carry it, and which request headers a browser may send.

This page lists the response headers the pirouter gateway writes on the inference endpoints and the request headers it reads. They are the same for all three protocols.

## Response headers

The table below is generated from the API specification, and the descriptions are its own text. Each header is listed with the response statuses it is sent with; `default` stands for the other error statuses the specification does not list one by one (`500`, `503` and so on).

| Header                          | Sent with                                                         | Description                                                                                                                                                                                                                                                                  |
| ------------------------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `X-Pi-Trace-Id`                 | `200`, `400`, `401`, `402`, `404`, `413`, `422`, `429`, `default` | Platform request id, minted by the gateway. Present on every response of the inference endpoints; quote it when reporting a problem.                                                                                                                                         |
| `X-Pi-Provider`                 | `200`, `default`                                                  | Provider that served this response, in the same vocabulary as `provider.order` and `GET /v1/models/{vendor}/{model}/endpoints`. Present only when the upstream provider answered (a completion or an upstream error); after a fallback it names the provider that took over. |
| `X-Pi-Model`                    | `200`, `default`                                                  | Model slug that served this response - the same value as the body's `model`. When a fallback model took over, it is that model. Present only when the upstream provider answered.                                                                                            |
| `X-Pi-Upstream-Model`           | `200`, `default`                                                  | The full model id the upstream provider itself reported in its response, passed on verbatim (on a stream, from the first data event). Absent when the upstream response named no model; never filled from the platform's own records.                                        |
| `X-Pi-Upstream-Request-Id`      | `200`, `default`                                                  | The upstream provider's own request id, passed on verbatim, for looking the call up on the provider's side. Absent when the provider returned none.                                                                                                                          |
| `Access-Control-Allow-Origin`   | `200`, `400`, `401`, `402`, `404`, `413`, `422`, `429`, `default` | Always `*`. Credentials are the API key header, never a cookie.                                                                                                                                                                                                              |
| `Access-Control-Expose-Headers` | `200`, `400`, `401`, `402`, `404`, `413`, `422`, `429`, `default` | Lists every `X-Pi-*` response header plus `Retry-After` and `Content-Encoding`, so browser code can read them.                                                                                                                                                               |

### Which responses carry `X-Pi-Provider`

`X-Pi-Provider`, `X-Pi-Model`, `X-Pi-Upstream-Model` and `X-Pi-Upstream-Request-Id` appear only on responses a provider actually answered, whether it succeeded or returned an error. `X-Pi-Upstream-Model` and `X-Pi-Upstream-Request-Id` also need the provider to have sent that value.

These responses carry none of the four, only `X-Pi-Trace-Id` and the CORS headers:

- The gateway's own refusals: authentication, balance, rate limit, validation, no available provider.
- Requests the provider took more than 90 seconds to start answering. Their headers were sent before any provider answered. See [Long requests](https://pirouter.ai/docs/errors.md#long-requests).

One exception: when a provider refuses pirouter's own credential (an upstream `401` or `403`, not a problem with your key) and no other provider takes over, you get `503` with the error code `provider_unavailable`. That response carries `X-Pi-Provider` and `X-Pi-Model`, naming the provider that refused, and neither `X-Pi-Upstream-*` header.

### `Retry-After`

When pirouter rate limits a request (`429`, error code `rate_limited`), the response carries `Retry-After`: the number of seconds until the next full minute, from 1 to 60. See [Rate limits](https://pirouter.ai/docs/rate-limits.md).

### Provider response headers

The provider's own response headers are not passed on, except these five: `Content-Type`, `Content-Length`, `Content-Encoding`, `Cache-Control` and `Retry-After`. So a `Retry-After` the provider sends with its own `429` or `503` reaches you. A streamed response has no `Content-Length`. An error the gateway writes in place of the provider's (such as the `503` above) carries none of the five.

### Fixed security headers

Every response carries these headers with these values:

| Header                      | Value                                      |
| --------------------------- | ------------------------------------------ |
| `X-Content-Type-Options`    | `nosniff`                                  |
| `X-Frame-Options`           | `DENY`                                     |
| `Referrer-Policy`           | `strict-origin-when-cross-origin`          |
| `Content-Security-Policy`   | `frame-ancestors 'none'`                   |
| `Permissions-Policy`        | `camera=(), microphone=(), geolocation=()` |
| `Strict-Transport-Security` | `max-age=31536000; includeSubDomains`      |

## Request headers

### `Authorization` and `X-Api-Key`

Send the key in `Authorization: Bearer` or in `X-Api-Key`; when both are present, `Authorization` wins. `x-goog-api-key` is not read. The order the gateway reads them in, and how each SDK sends them, is in [Send the key with a request](https://pirouter.ai/docs/authentication.md#send-the-key-with-a-request).

### Optional request headers

All three headers below are optional, and none of them changes the content of the answer.

| Header            | What pirouter does with it                                                                                                                                                                                                                                                                                              |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `X-Pi-Session-Id` | A session ID of up to 256 characters; a longer one counts as absent. Requests with the same workspace, key, model and session ID go first to the upstream deployment this session was last routed to, as long as it still meets the routing rules. The pin lasts 1 hour and restarts each time a request is admitted    |
| `X-Title`         | Your app's name. pirouter removes control and invisible characters, keeps at most 128 characters, and records it. The **Tools** ranking on the console's **Activity** page uses it, together with `HTTP-Referer`, to recognize common apps and clients; anything it does not recognize goes under **Unrecognized apps** |
| `HTTP-Referer`    | Your app's URL. Only the origin (scheme, host and port) is recorded, and only `http` and `https` are accepted; without this header the standard `Referer` is read. A value that does not qualify is simply not recorded, and the request is handled as usual                                                            |

These three headers are forwarded to the provider along with the request, so do not put anything sensitive in them.

The console's **Logs** do not show these three values today.

## Calling from a browser

The `OPTIONS` preflight a browser sends needs no key. The gateway answers it with `204`, the allowed methods (`POST, GET, OPTIONS`), the allowed request headers and `Access-Control-Max-Age: 86400`.

The allowed request headers are a fixed list: the credential headers of the three protocols (`Authorization`, `X-Api-Key`, `x-goog-api-key`), `Content-Type`, `anthropic-version`, `anthropic-beta` and `anthropic-dangerous-direct-browser-access`, the `X-Title`, `HTTP-Referer` and `X-Pi-Session-Id` headers above, and the `OpenAI-Organization`, `OpenAI-Project` and `X-Stainless-*` headers the official SDKs add in a browser. A header outside the list makes the browser's preflight fail; calls from a server are not limited by it.

`Access-Control-Allow-Origin` is `*`: the credential travels in a header, never in a cookie. `Access-Control-Expose-Headers` lists every `X-Pi-*` response header plus `Retry-After` and `Content-Encoding`, so browser code can read them.

## Next steps

- [Rate limits](https://pirouter.ai/docs/rate-limits.md): When a 429 appears and how long to wait before retrying.
- [Errors](https://pirouter.ai/docs/errors.md): What each error code means and how to handle it.
