[metadata]
apple-mobile-web-app-title: OpenRouter | Documentation
application-name: OpenRouter | Documentation
description: API Errors and Debugging
generator: Mintlify
msapplication-TileColor: #7624F4
msapplication-config: /docs/_mintlify/favicons/openrouter-d02e98a0/EmhfYwFO2tvnkuV-/_generated/favicon/browserconfig.xml
og:description: Learn how to handle errors and debug requests in OpenRouter API interactions. Comprehensive guide to error codes, messages, debugging options, and best practices.
og:image: https://openrouter.ai/dynamic-og?title=API%20Error%20Handling%20and%20Debugging&description=Learn%20how%20to%20handle%20errors%20and%20debug%20requests%20in%20OpenRouter%20API%20interactions.
og:image:height: 630
og:image:width: 1200
og:site_name: OpenRouter Documentation
og:title: API Error Handling and Debugging - Complete Guide
og:type: website
og:url: https://openrouter.ai/docs/api_reference/errors-and-debugging
twitter:card: summary_large_image
twitter:description: Learn how to handle errors and debug requests in OpenRouter API interactions. Comprehensive guide to error codes, messages, debugging options, and best practices.
twitter:image: https://openrouter.ai/dynamic-og?title=API%20Error%20Handling%20and%20Debugging&description=Learn%20how%20to%20handle%20errors%20and%20debug%20requests%20in%20OpenRouter%20API%20interactions.
twitter:image:height: 630
twitter:image:width: 1200
twitter:title: API Error Handling and Debugging - Complete Guide
viewport: width=device-width, initial-scale=1, viewport-fit=cover

[canonical-links]
https://openrouter.ai/docs/api_reference/errors-and-debugging

[document-links]
/docs/llms.txt: /docs/llms.txt
API Changelog: /docs/changelog
API Reference: /docs/api_reference/overview
API Versioning: /docs/api_reference/versioning
Add credits: https://openrouter.ai/credits
Agent SDK: /docs/agent-sdk/overview
Apps: https://openrouter.ai/apps
Authentication: /docs/api_reference/authentication
Benchmarks: https://openrouter.ai/benchmarks
Chat: https://openrouter.ai/chat
Client SDKs: /docs/client-sdks/overview
Cookbook: /docs/cookbook/get-started/quickstart
Docs: /docs/quickstart
Embeddings: /docs/api_reference/embeddings
Errors and Debugging: /docs/api_reference/errors-and-debugging
Limits: /docs/api_reference/limits
Models: https://openrouter.ai/models
OpenRouter | Documentation home page: https://openrouter.ai
Overview: /docs/api_reference/overview
Overview: /docs/api_reference/responses/overview
Parameters: /docs/api_reference/parameters
Pipeline Stages: /docs/guides/features/router-metadata#pipeline-stages
Rankings: https://openrouter.ai/rankings
Sign In: https://openrouter.ai/sign-in
Streaming: /docs/api_reference/streaming
fallback routing: /docs/guides/routing/provider-selection
guardrail: /docs/guides/features/guardrails
guardrails: /docs/guides/features/guardrails
router metadata: /docs/guides/features/router-metadata

[structured-data]
{"@context":"https://schema.org","@graph":[{"@id":"https://openrouter.ai/#organization","@type":"Organization","logo":{"@type":"ImageObject","url":"https://mintcdn.com/openrouter-d02e98a0/ksNSeB_K7gD-BUDh/assets/logo-v2-light.svg?fit=max&auto=format&n=ksNSeB_K7gD-BUDh&q=85&s=690e45873843519efe02bcbe45795dac"},"name":"OpenRouter | Documentation","url":"https://openrouter.ai"},{"@id":"https://openrouter.ai/docs#website","@type":"WebSite","name":"OpenRouter | Documentation","publisher":{"@id":"https://openrouter.ai/#organization"},"url":"https://openrouter.ai/docs"},{"@id":"https://openrouter.ai/docs/api_reference/errors-and-debugging#webpage","@type":"WebPage","breadcrumb":{"@id":"https://openrouter.ai/docs/api_reference/errors-and-debugging#breadcrumb"},"dateModified":"2026-08-31T22:02:12.842Z","description":"API Errors and Debugging","isPartOf":{"@id":"https://openrouter.ai/docs#website"},"name":"Errors and Debugging","url":"https://openrouter.ai/docs/api_reference/errors-and-debugging"},{"@id":"https://openrouter.ai/docs/api_reference/errors-and-debugging#breadcrumb","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","item":"https://openrouter.ai/docs/api_reference/overview","name":"API Guides","position":1},{"@type":"ListItem","item":"https://openrouter.ai/docs/api_reference/errors-and-debugging","name":"Errors and Debugging","position":2}]},{"@id":"https://openrouter.ai/docs/api_reference/errors-and-debugging#article","@type":["Article","TechArticle"],"dateModified":"2026-08-31T22:02:12.842Z","description":"API Errors and Debugging","headline":"Errors and Debugging","image":"https://openrouter.ai/dynamic-og?title=API%20Error%20Handling%20and%20Debugging&description=Learn%20how%20to%20handle%20errors%20and%20debug%20requests%20in%20OpenRouter%20API%20interactions.","isPartOf":{"@id":"https://openrouter.ai/docs#website"},"mainEntityOfPage":{"@id":"https://openrouter.ai/docs/api_reference/errors-and-debugging#webpage"},"name":"Errors and Debugging","publisher":{"@id":"https://openrouter.ai/#organization"},"url":"https://openrouter.ai/docs/api_reference/errors-and-debugging"}]}

[content]
API Error Handling and Debugging - Complete Guide
Documentation Index
Fetch the complete documentation index at:
/docs/llms.txt
Use this file to discover all available pages before exploring further.
Skip to main content
OpenRouter | Documentation
home page
Search...
⌘
K
Ask Assistant
⌘
I
Models
Benchmarks
Chat
Rankings
Apps
Sign In
Sign In
Search...
Navigation
API Guides
Errors and Debugging
Docs
API Reference
Client SDKs
Agent SDK
Cookbook
API Guides
Overview
Streaming
Embeddings
Limits
Authentication
Parameters
Errors and Debugging
Responses API
Versioning
API Versioning
API Changelog
API Reference
Analytics
TTS
STT
OAuth
Benchmarks
BYOK
Chat
Classifications
Containers
Credits
Datasets
Embeddings
Endpoints
Files
Generations
Guardrails
Images
API Keys
Anthropic Messages
Models
Observability
Organization
Presets
Providers
Rerank
Responses
SCIM
Video Generation
Workspaces
On this page
Error codes
Retry-After header
Moderation errors
Guardrail errors
Provider errors
Masking and raw provider details
When no content is generated
Streaming error formats
Pre-stream errors
Mid-stream errors
Non-streaming requests
Typed error codes
Token and length limits
Authentication and authorization
Rate limiting and availability
Request validation
Content policy
Image errors
Generic
Skin-specific error formats
Chat Completions (/api/v1/chat/completions)
Responses API (/api/v1/responses)
Error code transformations
Anthropic Messages (/api/v1/messages)
Debugging
Debug option shape
Usage
Chat Completions
Responses API
Debug response format
Chat Completions
Responses API
Important notes
Use cases
Privacy and redaction
API Guides
Errors and Debugging
Copy page
Copy page
API Errors and Debugging
Copy page
Copy page
For errors, OpenRouter returns a JSON response with the following shape:
type
ErrorResponse
=
{
error
:
{
code
:
number
;
message
:
string
;
metadata
?:
Record
<
string
,
unknown
>;
};
};
The HTTP Response will have the same status code as
error.code
, forming a request error if:
Your original request is invalid
Your API key/account is out of credits
Otherwise, the returned HTTP response status will be
and any error occurred while the LLM is producing the output will be emitted in the response body or as an SSE data event.
Example code for printing errors in JavaScript:
const
request
=
await
fetch
(
'https://openrouter.ai/...'
);
console
.
log
(
request
.
status
);
// Will be an error code unless the model started processing your request
const
response
=
await
request
.
json
();
console
.
error
(
response
.
error
?.
code
);
// Will be an error code
console
.
error
(
response
.
error
?.
message
);
​
Error codes
: Bad Request (invalid or missing params, CORS)
: Invalid credentials (OAuth session expired, disabled/invalid API key)
: Your account or API key has insufficient credits. Add more credits and retry the request.
: Forbidden (insufficient permissions, guardrail block, or moderation flag)
: Your request timed out
: You are being rate limited
: Your chosen model is down or we received an invalid response from it
: There is no available model provider that meets your routing requirements
​
Retry-After header
On
and
responses, OpenRouter may include a standard HTTP
Retry-After
response header indicating how many seconds to wait before retrying.
HTTP
/
1.1
429
Too Many Requests
Retry-After
:
60
The OpenAI SDK, Anthropic SDK, Vercel AI SDK, and OpenRouter SDK already respect this header for backoff. If you’re using
fetch
directly, honor it before retrying:
const
res
=
await
fetch
(
'https://openrouter.ai/api/v1/chat/completions'
, {
...
});
if
(
res
.
status
===
429
||
res
.
status
===
503
) {
const
retryAfter
=
Number
(
res
.
headers
.
get
(
'Retry-After'
));
if
(
Number
.
isFinite
(
retryAfter
)
&&
retryAfter
>
0
) {
await
new
Promise
((
r
)
=>
setTimeout
(
r
,
retryAfter
*
1000
));
// retry the request
}
}
​
Moderation errors
If your input was flagged, the
error.metadata
will contain information about the issue. The shape of the metadata is as follows:
type
ModerationErrorMetadata
=
{
reasons
:
string
[];
// Why your input was flagged
flagged_input
:
string
;
// The text segment that was flagged, limited to 100 characters. If the flagged input is longer than 100 characters, it will be truncated in the middle and replaced with ...
provider_name
:
string
;
// The name of the provider that requested moderation
model_slug
:
string
;
};
​
Guardrail errors
On inference endpoints (
/chat/completions
,
/responses
,
/messages
), a request can be blocked before it reaches a provider — for example by a content filter or prompt-injection detector configured via
guardrails
. When this happens, the response is a
403
with a message describing the block reason. The OpenRouter-shaped body below applies to
/chat/completions
and
/responses
; on
/messages
the block is returned as an Anthropic
permission_error
envelope (see
Anthropic Messages
below), with
openrouter_metadata
still carried at the top level when opted in:
{
"error"
: {
"code"
:
403
,
"message"
:
"Request blocked: prompt injection patterns detected"
,
"metadata"
: {
"patterns"
: [
"ignore all previous instructions"
]
}
}
}
When you opt in to
router metadata
via the
X-OpenRouter-Experimental-Metadata: enabled
header, the 403 response also includes the full
openrouter_metadata
object with routing context and a
pipeline
array detailing the guardrail stages that ran:
{
"error"
: {
"code"
:
403
,
"message"
:
"Request blocked: prompt injection patterns detected"
,
"metadata"
: {
"patterns"
: [
"ignore all previous instructions"
]
}
},
"openrouter_metadata"
: {
"requested"
:
"openai/gpt-4o"
,
"strategy"
:
"direct"
,
"region"
:
"iad"
,
"summary"
:
"available=1"
,
"attempt"
:
1
,
"is_byok"
:
false
,
"endpoints"
: {
"total"
:
1
,
"available"
: [
{
"provider"
:
"OpenAI"
,
"model"
:
"openai/gpt-4o"
,
"selected"
:
false
}
]
},
"pipeline"
: [
{
"type"
:
"guardrail"
,
"name"
:
"regex_pi_detection"
,
"guardrail_id"
:
"grd_abc123"
,
"guardrail_scope"
:
"api-key"
,
"summary"
:
"Blocked: prompt injection detected (1 pattern matched)"
,
"data"
: {
"action"
:
"blocked"
,
"detected"
:
true
,
"engines"
: [
"regex"
],
"patterns"
: [
"ignore all previous instructions"
]
}
}
]
}
}
See all 38 lines
The
openrouter_metadata
object follows the same shape as on successful responses — see
Pipeline Stages
for the full stage type and field reference.
​
Provider errors
OpenRouter normalizes every upstream provider error into the stable, typed
error_type
vocabulary documented under
Typed Error Codes
. The same
error_type
values describe what went wrong whether the provider error arrives in a non-streaming response body or as a mid-stream SSE event. Native protocol codes (the Anthropic
error.type
, the Responses
error.code
) are best-effort and can differ between formats —
error_type
is the field to rely on across all of them.
For Chat Completions, a provider error that interrupts generation carries
error_type
inside
error.metadata
:
{
"error"
: {
"code"
:
429
,
"message"
:
"Rate limit exceeded"
,
"metadata"
: {
"error_type"
:
"rate_limit_exceeded"
,
"provider_code"
:
"rate_limited"
}
}
}
The same value is carried on mid-stream errors and on the Anthropic and Responses skins — see
Skin-Specific Error Formats
for the exact wire location in each format.
​
Masking and raw provider details
When a request fails with a
500
, the
message
is replaced with a generic string and
provider_code
and
openrouter_metadata
are omitted, but
error_type
is still present (
server
).
For non-500 errors, the upstream provider’s own error code is surfaced in
error.metadata.provider_code
when available. Opt-in routing context (which provider was selected, fallback attempts, etc.) is carried in the
openrouter_metadata
object when the request sets
X-OpenRouter-Metadata
— it follows the same shape as on successful responses (routing-summary fields only; see
Pipeline Stages
).
​
When no content is generated
Occasionally, the model may not generate any content. This typically occurs when:
The model is warming up from a cold start
The system is scaling up to handle more requests
Warm-up times usually range from a few seconds to a few minutes, depending on the model and provider.
If you encounter persistent no-content issues, consider implementing a simple retry mechanism or trying again with a different provider or model that has more recent activity.
In some cases, you may still be charged for the prompt processing cost by the upstream provider, even if no content is generated.
​
Streaming error formats
When using streaming mode (
stream: true
), errors are handled differently depending on when they occur:
​
Pre-stream errors
Errors that occur before any tokens are sent follow the standard error format above, with appropriate HTTP status codes. At this stage the HTTP response hasn’t been committed yet, so OpenRouter can:
Return a proper HTTP error status (4xx/5xx)
Silently retry with a different provider endpoint if
fallback routing
is enabled
Apply rate-limit or auth checks before any work begins
You’ll see pre-stream errors for issues like invalid API keys, malformed requests, or when every available provider endpoint is exhausted before streaming starts.
​
Mid-stream errors
OpenRouter sends you the HTTP
200 OK
status and headers as soon as the provider accepts the request. That happens before the model produces a single token, and an HTTP status is final once sent.
Every failure after that point is therefore reported inside the response instead of in the status. A streaming request receives an SSE error event; a non-streaming request receives an error body. This includes provider errors that arrive before any token. Failover also stops once part of the answer has reached you, since your application already holds output from the first provider.
Common causes of mid-stream errors:
Provider disconnect
— the upstream connection drops after partial output (network issue, provider crash, load balancer timeout)
Provider timeout
— the model stops responding mid-generation and the read deadline expires
Token limit hit during generation
— the model reaches
max_tokens
or the context window fills up while producing output
Output content filter
— a content moderation system flags generated text after some of it was already streamed
Provider overload
— the upstream returns a rate-limit or capacity error after beginning to stream
If an attempt fails before any tokens reach you, OpenRouter automatically tries a backup provider. The
200 OK
has already been sent by then, so the status stays
200
even when every provider fails — the last error reaches you in the response body.
Mid-stream errors are sent as Server-Sent Events (SSE) with a unified structure that includes both the error details and a completion choice:
type
MidStreamError
=
{
id
:
string
;
object
:
'chat.completion.chunk'
;
created
:
number
;
model
:
string
;
provider
:
string
;
error
:
{
code
:
number
;
// HTTP status code (e.g. 400, 429, 502)
message
:
string
;
metadata
?:
{
error_type
:
string
;
// Typed error code — see table below
provider_code
?:
string
;
// Original upstream error code (omitted on 500s)
};
};
choices
:
[{
index
:
0
;
delta
:
{
content
:
''
};
finish_reason
:
'error'
;
native_finish_reason
?:
string
;
}];
};
See all 21 lines
Example SSE data:
data: {"id":"gen-abc123","object":"chat.completion.chunk","created":1234567890,"model":"openai/gpt-4o","provider":"OpenAI","error":{"code":429,"message":"Rate limit exceeded","metadata":{"error_type":"rate_limit_exceeded"}},"choices":[{"index":0,"delta":{"content":""},"finish_reason":"error"}]}
Key characteristics:
The error appears at the
top level
alongside standard response fields
error.metadata.error_type
carries a typed code you can switch on programmatically — see
Typed Error Codes
for the full list
A
choices
array is included with
finish_reason: "error"
to properly terminate the stream
The HTTP status remains
200 OK
, because the headers were sent before the error occurred
The stream is terminated after this event
On 500-class errors,
error.message
is replaced with a generic string and
provider_code
is omitted to prevent leaking upstream details
​
Non-streaming requests
Non-streaming requests send the status at the same point. If the provider returns headers and then fails, you receive a
200 OK
whose JSON body holds only an
error
object and no
choices
; its
id
identifies the generation when you report the failure. Check the body for an
error
field even on a
200
, rather than relying on the status alone.
​
Typed error codes
When a provider error reaches your application, OpenRouter tags it with a canonical
error_type
string — both on the non-streaming response body and on mid-stream SSE events. Use this value, not the HTTP status code alone, to programmatically distinguish error categories. It is stable across all three API skins even when the native protocol code is lossy.
Where
error_type
appears depends on the skin and path:
Chat Completions
:
error.metadata.error_type
— on the mid-stream error chunk (see
Mid-Stream Errors
) and on the non-streaming response when a provider error interrupts generation.
Anthropic Messages
:
error.error_type
on the SSE
error
event and the non-streaming error envelope.
Responses
: top-level
error_type
on the failed response, for both the streaming
response.failed
event and the non-streaming JSON body.
The HTTP status each
error_type
maps to is listed in the tables below.
​
Token and length limits
error_type
HTTP Status
Description
context_length_exceeded
The combined input and output tokens exceed the model’s context window.
max_tokens_exceeded
Generation stopped because
max_tokens
(or
max_completion_tokens
) was reached.
token_limit_exceeded
A token budget enforced by OpenRouter (e.g. credit-based cap) was exceeded.
string_too_long
A single string field in the request (system prompt, user message, etc.) exceeded the provider’s per-field character limit.
​
Authentication and authorization
error_type
HTTP Status
Description
authentication
The API key is missing, invalid, or revoked.
permission_denied
The key is valid but lacks the required permission or the request was blocked by a
guardrail
.
payment_required
The account or API key has insufficient credits.
Add credits
and retry.
​
Rate limiting and availability
error_type
HTTP Status
Description
rate_limit_exceeded
Request- or token-level rate limit hit. Respect the
Retry-After
header before retrying.
provider_overloaded
The upstream provider is temporarily overloaded. Retry after a short delay.
provider_unavailable
The upstream provider returned an invalid or empty response. OpenRouter may auto-retry with another provider if fallback routing is enabled.
​
Request validation
error_type
HTTP Status
Description
invalid_request
A request parameter is malformed or missing.
invalid_prompt
A specific message in the
messages
array is invalid (e.g. unsupported role, empty content).
not_found
The requested resource (model, file, etc.) does not exist.
precondition_failed
A precondition header (e.g.
If-Match
) was not satisfied.
payload_too_large
The request body exceeds the maximum allowed size.
unprocessable
The request is syntactically valid but semantically unprocessable.
​
Content policy
error_type
HTTP Status
Description
content_policy_violation
The input or output was flagged by a content filter (provider- or OpenRouter-level).
refusal
The model explicitly refused to comply with the request (e.g. safety refusal).
​
Image errors
error_type
HTTP Status
Description
invalid_image
An image in the request is corrupt or unreadable.
image_too_large
An image exceeds the provider’s maximum file size or pixel dimensions.
image_too_small
An image is below the provider’s minimum pixel dimensions.
unsupported_image_format
The image format is not supported by the provider.
image_not_found
The referenced image URL or file ID could not be resolved.
image_download_failed
OpenRouter could not download the image from the provided URL (DNS failure, timeout, non-200 response, etc.).
​
Generic
error_type
HTTP Status
Description
server
An unexpected internal error. The upstream error message is masked on this type.
timeout
The provider did not respond within the allowed time.
unmapped
An upstream error that doesn’t map to any known category.
error.metadata.provider_code
may contain the original code.
​
Skin-specific error formats
OpenRouter exposes three API skins. Each translates the same internal provider error types into its own wire format, for non-streaming responses and in-stream errors alike. In every case
error_type
is the stable field; the wire location differs per skin.
​
Chat Completions (
/api/v1/chat/completions
)
Mid-stream errors appear as a
chat.completion.chunk
with a top-level
error
object (shape shown
above
). The
error.metadata.error_type
field carries the typed code.
For non-streaming requests where a provider error occurs, the error is embedded in the final response alongside any partial content:
{
"choices"
: [{
"message"
: {
"role"
:
"assistant"
,
"content"
:
"partial output..."
},
"finish_reason"
:
"error"
,
"error"
: {
"code"
:
502
,
"message"
:
"Provider disconnected mid-stream"
,
"metadata"
: {
"error_type"
:
"provider_unavailable"
}
}
}]
}
​
Responses API (
/api/v1/responses
)
The Responses API maps internal error types to the OpenAI Responses error code set. The mapping is narrower — many distinct internal types collapse to
server_error
— so the precise reason is preserved in a top-level
error_type
field on the response, outside the native
error
object:
Internal
error_type
Responses API
code
rate_limit_exceeded
rate_limit_exceeded
context_length_exceeded
,
invalid_request
invalid_prompt
content_policy_violation
image_content_policy_violation
authentication
,
provider_overloaded
,
provider_unavailable
,
timeout
,
server
server_error
All others (including
invalid_prompt
)
server_error
Both the streaming terminal event and the non-streaming JSON body carry the canonical
error_type
at the top level of the response object. For example, an authentication failure collapses to the native
server_error
code but keeps
error_type: "authentication"
:
{
"id"
:
"resp_abc123"
,
"status"
:
"failed"
,
"error"
: {
"code"
:
"server_error"
,
"message"
:
"Invalid credentials"
},
"error_type"
:
"authentication"
}
Streaming errors surface as one of three SSE event types, each wrapping the same response object:
response.failed
— terminal event when the response could not complete:
{
"type"
:
"response.failed"
,
"response"
: {
"id"
:
"resp_abc123"
,
"status"
:
"failed"
,
"error"
: {
"code"
:
"server_error"
,
"message"
:
"Internal server error"
},
"error_type"
:
"server"
}
}
response.error
— error during response generation:
{
"type"
:
"response.error"
,
"error"
: {
"code"
:
"rate_limit_exceeded"
,
"message"
:
"Rate limit exceeded"
}
}
error
— plain error event (matches upstream OpenAI behavior):
{
"type"
:
"error"
,
"error"
: {
"code"
:
"invalid_api_key"
,
"message"
:
"Invalid API key provided"
}
}
​
Error code transformations
Certain token/length errors are transformed into successful completions instead of failures:
error_type
Transformed To
Finish Reason
context_length_exceeded
Success
length
max_tokens_exceeded
Success
length
token_limit_exceeded
Success
length
string_too_long
Success
length
This allows graceful handling of limit-based errors without treating them as failures.
​
Anthropic Messages (
/api/v1/messages
)
The Anthropic Messages skin maps internal types to Anthropic-native error type strings:
Internal
error_type
Anthropic
error.type
authentication
authentication_error
permission_denied
permission_error
payment_required
billing_error
not_found
,
image_not_found
not_found_error
rate_limit_exceeded
rate_limit_error
provider_overloaded
overloaded_error
timeout
timeout_error
context_length_exceeded
,
content_policy_violation
,
invalid_request
, …
invalid_request_error
provider_unavailable
,
server
,
unmapped
, …
api_error
Because the native
error.type
is lossy (many internal types collapse to
api_error
), the canonical
error_type
is added inside the
error
object alongside it. This holds for both the non-streaming error envelope and mid-stream SSE
error
events.
Non-streaming error envelope (pre-Router errors such as authentication failures carry a
null
request_id
; post-Router errors carry the
gen-
generation ID):
{
"type"
:
"error"
,
"error"
: {
"type"
:
"authentication_error"
,
"message"
:
"Invalid credentials"
,
"error_type"
:
"authentication"
},
"request_id"
:
null
}
Mid-stream errors are emitted as an SSE
error
event with the same shape:
{
"type"
:
"error"
,
"error"
: {
"type"
:
"overloaded_error"
,
"message"
:
"Provider is temporarily overloaded"
,
"error_type"
:
"provider_overloaded"
}
}
​
Debugging
OpenRouter provides a
debug
option that allows you to inspect the exact request body that was sent to the upstream provider. This works with both the Chat Completions API (
/api/v1/chat/completions
) and the Responses API (
/api/v1/responses
). Useful for understanding how OpenRouter transforms your request parameters for different providers.
​
Debug option shape
The debug option is an object with the following shape:
type
DebugOptions
=
{
echo_upstream_body
?:
boolean
;
// If true, returns the transformed request body sent to the provider
};
​
Usage
To enable debug output, include the
debug
parameter in your request:
​
Chat Completions
TypeScript
Python
fetch
(
'https://openrouter.ai/api/v1/chat/completions'
, {
method:
'POST'
,
headers:
{
Authorization:
'Bearer <OPENROUTER_API_KEY>'
,
'Content-Type'
:
'application/json'
,
},
body:
JSON
.
stringify
({
model:
'anthropic/claude-haiku-4.5'
,
stream:
true
,
// Debug only works with streaming
messages:
[
{
role:
'system'
,
content:
'You are a helpful assistant.'
},
{
role:
'user'
,
content:
'Hello!'
},
],
debug:
{
echo_upstream_body:
true
,
},
}),
});
const
text
=
await
response
.
text
();
for
(
const
line
of
text
.
split
(
'
\n
'
)) {
if
(
!
line
.
startsWith
(
'data: '
))
continue
;
const
data
=
line
.
slice
(
6
);
if
(
data
===
'[DONE]'
)
break
;
const
parsed
=
JSON
.
parse
(
data
);
if
(
parsed
.
debug
?.
echo_upstream_body
) {
console
.
log
(
'
\n
Debug:'
,
JSON
.
stringify
(
parsed
.
debug
.
echo_upstream_body
,
null
,
2
));
}
process
.
stdout
.
write
(
parsed
.
choices
?.[
0
]?.
delta
?.
content
??
''
);
}
See all 35 lines
import
requests
import
json
response
=
requests.post(
url
=
"https://openrouter.ai/api/v1/chat/completions"
,
headers
=
{
"Authorization"
:
"Bearer <OPENROUTER_API_KEY>"
,
"Content-Type"
:
"application/json"
,
},
data
=
json.dumps({
"model"
:
"anthropic/claude-haiku-4.5"
,
"stream"
:
True
,
"messages"
: [
{
"role"
:
"system"
,
"content"
:
"You are a helpful assistant."
},
{
"role"
:
"user"
,
"content"
:
"Hello!"
}
],
"debug"
: {
"echo_upstream_body"
:
True
}
}),
stream
=
True
)
for
line
in
response.iter_lines():
if
line:
text
=
line.decode(
'utf-8'
)
if
'echo_upstream_body'
in
text:
print
(text)
​
Responses API
TypeScript
Python
fetch
(
'https://openrouter.ai/api/v1/responses'
, {
method:
'POST'
,
headers:
{
Authorization:
'Bearer <OPENROUTER_API_KEY>'
,
'Content-Type'
:
'application/json'
,
},
body:
JSON
.
stringify
({
model:
'anthropic/claude-haiku-4.5'
,
stream:
true
,
input:
'Hello!'
,
debug:
{
echo_upstream_body:
true
,
},
}),
});
const
text
=
await
response
.
text
();
for
(
const
line
of
text
.
split
(
'
\n
'
)) {
if
(
!
line
.
startsWith
(
'data: '
))
continue
;
const
data
=
line
.
slice
(
6
);
if
(
data
===
'[DONE]'
)
break
;
const
parsed
=
JSON
.
parse
(
data
);
if
(
parsed
.
type
===
'response.debug'
) {
console
.
log
(
'
\n
Debug:'
,
JSON
.
stringify
(
parsed
.
debug
,
null
,
2
));
}
}
See all 30 lines
import
requests
import
json
response
=
requests.post(
url
=
"https://openrouter.ai/api/v1/responses"
,
headers
=
{
"Authorization"
:
"Bearer <OPENROUTER_API_KEY>"
,
"Content-Type"
:
"application/json"
,
},
data
=
json.dumps({
"model"
:
"anthropic/claude-haiku-4.5"
,
"stream"
:
True
,
"input"
:
"Hello!"
,
"debug"
: {
"echo_upstream_body"
:
True
}
}),
stream
=
True
)
for
line
in
response.iter_lines():
if
line:
text
=
line.decode(
'utf-8'
)
if
'response.debug'
in
text:
print
(text)
​
Debug response format
​
Chat Completions
When
debug.echo_upstream_body
is set to
true
, OpenRouter sends a debug chunk as the
first chunk
in the streaming response. This chunk has an empty
choices
array and includes a
debug
field with the transformed request body:
{
"id"
:
"gen-xxxxx"
,
"provider"
:
"Anthropic"
,
"model"
:
"anthropic/claude-haiku-4.5"
,
"object"
:
"chat.completion.chunk"
,
"created"
:
1234567890
,
"choices"
: [],
"debug"
: {
"echo_upstream_body"
: {
"system"
: [
{
"type"
:
"text"
,
"text"
:
"You are a helpful assistant."
}
],
"messages"
: [
{
"role"
:
"user"
,
"content"
:
"Hello!"
}
],
"model"
:
"claude-haiku-4-5-20251001"
,
"stream"
:
true
,
"max_tokens"
:
64000
,
"temperature"
:
1
}
}
}
See all 22 lines
​
Responses API
On the Responses API, debug data arrives as a
response.debug
SSE event:
{
"type"
:
"response.debug"
,
"debug"
: {
"echo_upstream_body"
: {
"model"
:
"claude-haiku-4-5-20251001"
,
"messages"
: [
{
"role"
:
"user"
,
"content"
:
"Hello!"
}
],
"stream"
:
true
,
"max_tokens"
:
64000
,
"temperature"
:
1
}
},
"sequence_number"
:
0
}
​
Important notes
Streaming Only
The debug option
only works with streaming mode
(
stream: true
). Non-streaming requests will ignore the debug parameter.
Not for Production
The debug flag should
not be used in production environments
. It is intended for development and debugging purposes only, as it may potentially return sensitive information included in the request that was not intended to be visible elsewhere.
​
Use cases
The debug output is particularly useful for:
Understanding Parameter Transformations
: See how OpenRouter maps your parameters to provider-specific formats (e.g., how
max_tokens
is set, how
temperature
is handled).
Verifying Message Formatting
: Check how OpenRouter combines and formats your messages for different providers (e.g., how system messages are concatenated, how user messages are merged).
Checking Applied Defaults
: See what default values OpenRouter applies when parameters are not specified in your request.
Debugging Provider Fallbacks
: When using provider fallbacks, a debug chunk will be sent for
each attempted provider
, allowing you to see which providers were tried and what parameters were sent to each.
​
Privacy and redaction
OpenRouter will make a best effort to automatically redact potentially sensitive or noisy data from debug output. Remember that the debug option is not intended for production.
Parameters
Overview
Assistant
Responses are generated using AI and may contain mistakes.
