Chat completions passthrough

Relay a provider’s own chat-completions payload and response through Respan, untranslated.

POST /api/{provider}/v1/chat/completions

Relays a provider’s own chat-completions payload and returns that provider’s own response, byte for byte. Respan authenticates the request, applies your limits and budgets, and writes the log row. It does not rewrite the request or recompute usage.

Use this when correctness matters more than portability: agent frameworks pinned to one provider’s exact semantics, tuned tool-calling behavior, or any payload where a translation layer is a liability. For one request shape across many providers, use the unified router at POST /api/chat/completions instead. That endpoint normalizes; this one does not.

{provider} is the provider id (for example openai, xai, groq). Authorization carries your Respan key, not the provider’s.

$curl https://api.respan.ai/api/openai/v1/chat/completions \
> -H "Authorization: Bearer $RESPAN_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{
> "model": "gpt-5.5",
> "messages": [{"role": "user", "content": "Reply with exactly: CODE_OK"}],
> "tools": [{"type": "function", "function": {"name": "submit_patch", "parameters": {"type": "object"}}}],
> "tool_choice": {"type": "function", "function": {"name": "submit_patch"}},
> "reasoning_effort": "medium",
> "parallel_tool_calls": false,
> "store": false
> }'

Grok is the same call with a different path segment:

$curl https://api.respan.ai/api/xai/v1/chat/completions \
> -H "Authorization: Bearer $RESPAN_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{"model": "grok-4.3", "messages": [{"role": "user", "content": "hi"}]}'

With the OpenAI SDK, repoint base_url and pass your Respan key. Nothing else changes:

1from openai import OpenAI
2
3client = OpenAI(
4 api_key="YOUR_RESPAN_API_KEY",
5 base_url="https://api.respan.ai/api/openai/v1",
6)
7client.chat.completions.create(model="gpt-5.5", messages=[{"role": "user", "content": "Hi"}])

What is guaranteed

AspectBehavior
Request bodyForwarded unchanged. Unknown fields, new provider fields, and provider-only extensions all survive. Only respan_params is removed.
UsageThe provider’s own numbers, including cached-token and provider-specific fields. Never recomputed or estimated.
Invalid requestsStay invalid. store: false with metadata fails exactly as it fails calling the provider directly, rather than being normalized into a success.
Response bodyThe provider’s bytes, preserving key order and formatting. Streaming is relayed frame by frame.

Headers

Forwarded upstream: accept, accept-language, content-type, user-agent, plus that provider’s x-{provider}-* extension headers. Everything else (cookies, Respan’s own headers) is dropped, so nothing leaks to the provider.

Returned to you:

HeaderMeaning
X-Respan-Log-IdRespan log id for this request
X-Respan-Upstream-Request-IdThe provider’s own request id, for correlating with their support
x-ratelimit-*The provider’s rate-limit headers, relayed
retry-afterRelayed when the provider sends it

Errors and limits

Provider errors pass through with the provider’s status code and body, so your existing error handling keeps working. Respan’s own errors:

StatusMeaning
401No usable credentials for the provider
404Provider is not available as an OpenAI-shaped passthrough
429Your Respan key’s rate limit

Respan imposes no request-body size, context-length, or truncation limit below the provider’s own, and never silently truncates. If a provider limit is exceeded, the provider’s error is returned. The request timeout is 600s so long reasoning calls complete.

Supported providers

azure_deepseek, azure_openai, baseten, cerebras, chutes, deepseek, empiriolabs, featherless, fireworks, friendliai, gonka24, groq, inceptron, inference_net, ionet, makora, minimax, mistral, moonshot, morphllm, nebius, nextbit256, novita, openai, openai_compatible, openrouter, parasail, perplexity, qwen, tera, togetherai, wafer, xai, zai

Providers whose API is not OpenAI-shaped (Anthropic, Google, Bedrock, Cohere) have their own native passthrough endpoints. See Routing & passthrough.

OpenRouter

OpenRouter has its own path and uses your OpenRouter key rather than an org integration:

$curl https://api.respan.ai/api/openrouter/v1/chat/completions \
> -H "Authorization: Bearer $RESPAN_API_KEY" \
> -H "x-openrouter-api-key: $OPENROUTER_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{"model": "anthropic/claude-sonnet-4-5", "messages": [{"role": "user", "content": "hi"}]}'

Authorization is your Respan key; x-openrouter-api-key is your OpenRouter key. A missing OpenRouter key returns 400. OpenRouter’s attribution headers http-referer and x-title are forwarded so your app still appears on their rankings.

See Routing & passthrough for how the OpenRouter passthrough compares to the unified router, and when to reach for each.