Overview
All requests are made over HTTPS to:
https://api.neuralrouter.ai/v1Requests and responses follow the OpenAI Chat Completions schema exactly, the body is unmodified, so existing OpenAI client libraries work by setting only the base URL and API key.
Routing is controlled two ways, neither of which changes the request body: the model field accepts a routing alias, and optional X-NR-* request headers override behaviour per call. Every response carries X-NR-* headers describing the decision. See Routing headers.
Authentication
Pass your workspace key as a bearer token. Keys are prefixed sk-nr- and scoped to one workspace.
Authorization: Bearer sk-nr-xxxxxxxxxxxxxxxxxxxxChat completions
Generate a model response for a conversation. Request parameters:
| Field | Type | Description | |
|---|---|---|---|
| model | string | required | A concrete model id, or a routing alias: "auto", "cheapest", "fastest", or "best". |
| messages | array | required | Conversation messages in OpenAI chat format. |
| stream | boolean | optional | Stream the response as server-sent events. Defaults to false. |
| temperature | number | optional | Sampling temperature, 0–2. |
| max_tokens | integer | optional | Maximum tokens to generate. |
| tools | array | optional | Tool/function definitions, OpenAI-compatible. |
| response_format | object | optional | Force JSON output with { "type": "json_object" }. |
Example request body:
{
"model": "auto",
"messages": [
{ "role": "system", "content": "You are concise." },
{ "role": "user", "content": "Summarize routing in one line." }
],
"temperature": 0.7,
"max_tokens": 256,
"stream": false
}Example response:
{
"id": "nrc_8f2a1c...",
"object": "chat.completion",
"created": 1782000000,
"model": "claude-sonnet",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "Routing sends each request to the best model for your objective." },
"finish_reason": "stop"
}
],
"usage": { "prompt_tokens": 24, "completion_tokens": 16, "total_tokens": 40 }
}Routing headers
Routing is controlled with optional request headers, so the request body stays byte-for-byte OpenAI-compatible and any OpenAI SDK can send them without a schema override.
| Field | Type | Description | |
|---|---|---|---|
| X-NR-Pin | string | optional | Force one catalog model, bypassing policy selection. Equivalent to naming a concrete model in `model`, and takes precedence over X-NR-Allow. An unknown id returns 404. |
| X-NR-Allow | string | optional | Comma-separated model ids to restrict this request's candidate set. A routing preference, not an access boundary, the enforced limit is the key's allowed models. |
| X-NR-Objective | string | optional | Override the workspace objective for this request: lowest-cost, lowest-latency, highest-quality, or quality-per-dollar. Unrecognized values are ignored. |
| X-NR-Preset | string | optional | Apply a saved preset. Accepts name, name@version to pin a version, or name|env to resolve an environment. A missing or stale reference is ignored rather than erroring. |
| X-NR-Session | string | optional | Group related requests into one session for cache and routing affinity. Echoed back on the response. |
| X-NR-ZDR | string | optional | Set to "true" to restrict routing to providers under a zero-retention agreement. |
| X-NR-Region | string | optional | Caller region hint. Honored only as a fallback when the edge cannot derive a region, since a client-supplied value is spoofable. |
Alternatively, set the model field to a routing alias instead of a concrete id: auto defers to your workspace policy, cheapest selects on lowest cost, fastest on lowest time-to-first-token, and best on highest quality. Any other non-empty value is treated as a concrete model and pins the route.
curl https://api.neuralrouter.ai/v1/chat/completions \
-H "Authorization: Bearer $NEURALROUTER_API_KEY" \
-H "Content-Type: application/json" \
-H "X-NR-Objective: lowest-latency" \
-H "X-NR-Allow: gpt-4o,claude-sonnet" \
-d '{
"model": "auto",
"messages": [{ "role": "user", "content": "Hello!" }]
}'Every response carries the routing decision:
| Field | Type | Description | |
|---|---|---|---|
| X-NR-Model | string | optional | The model that actually served the request. |
| X-NR-Route-Reason | string | optional | Why that route was selected. |
| X-NR-Overhead-Ms | string | optional | Router overhead in milliseconds, excluding provider time. |
| X-NR-Trace-Id | string | optional | Trace id for this request; quote it in support requests. |
| X-NR-Cache | string | optional | Cache outcome. Present only when a cache was consulted. |
| X-NR-Session | string | optional | The session id the request was grouped under, your value, or one derived from the conversation prefix. |
X-NR-Model: claude-sonnet
X-NR-Route-Reason: lowest-latency within allow-list
X-NR-Overhead-Ms: 7
X-NR-Trace-Id: nrq_8f2a1c4b
X-NR-Cache: missModels
Returns the models available to your workspace, each with provider, pricing, context window, and live health.
{
"object": "list",
"data": [
{
"id": "claude-sonnet",
"provider": "Anthropic",
"context_window": 200000,
"input_per_m": 3.0,
"output_per_m": 15.0,
"health": "healthy"
}
]
}Embeddings
Create an embedding vector for the given input, OpenAI-compatible. Routing applies the same way, set model to a specific embedding model or "auto".
{
"model": "auto",
"input": "The quick brown fox."
}Streaming format
When stream is true, the response is a sequence of server-sent events. Each event is a data: line carrying a chat.completion.chunk, ending with data: [DONE].
data: {"choices":[{"delta":{"content":"Rou"}}]}
data: {"choices":[{"delta":{"content":"ting"}}]}
data: [DONE]Rate limits
Rate limits are per workspace and returned on every response via x-ratelimit-remaining and x-ratelimit-reset headers. A 429 indicates you should retry after the reset window with exponential backoff. Upstream provider 429s are absorbed by failover and do not count against your limit.
Errors
Errors use standard HTTP status codes and a JSON body of the form { "error": { "type", "message" } }.
| Status | Type | Description |
|---|---|---|
| 400 | invalid_request | Malformed request or missing required field. |
| 401 | unauthenticated | Missing or invalid API key. |
| 403 | forbidden | Key lacks access, or a guardrail blocked the request. |
| 402 | budget_exceeded | The workspace budget cap has been reached. |
| 404 | model_not_found | Requested model id is not available to this workspace. |
| 429 | rate_limited | Too many requests; retry with backoff. |
| 503 | no_provider_available | All candidates and fallbacks are unavailable. |
New to the API? Start with the Documentation.