This article is published in English.
OpenRouter Privacy and Cost: Provider Routing Settings That Matter
OpenRouter forwards your prompts to third-party hosts. Learn ZDR, only, data_collection, user IDs, price sorting and how the same model's cost varies by host.
Choosing a model and wiring up the API feels like the end of the job. The app starts answering, so it must be finished. Behind that single integration, though, two questions are still open. Who actually receives your users' data? And how much does that cost you? A model name answers neither.
What OpenRouter is, and what it isn't
OpenRouter is an API gateway. It accepts a request and forwards it to a provider that runs the model. It isn't a privacy layer. OpenRouter sees the prompt, and so does the host it forwards to, which makes two parties processing your content instead of one.
Open-weight models give you the widest choice of host. Closed models narrow it without removing it entirely, since the options become the vendor's own endpoints and its cloud partners. OpenRouter's Zero Data Retention (ZDR) documentation shows this for Anthropic: enforcing ZDR drops the first-party Anthropic endpoints, while Bedrock and Vertex stay available. The weights are the same, but the operator and the data policy are different.
The list gets long on the open-weight side. Calling Qwen doesn't have to mean calling Alibaba. When this snapshot was taken, OpenRouter listed Qwen3 Coder through Google Vertex, DeepInfra (Turbo), Venice, NovitaAI and Alibaba OpenSource. If you pick an independently hosted endpoint, the inference request goes to that host, and the lab that released the weights doesn't automatically get a copy.
Copy endpoint names exactly
Typos in endpoint names are a quiet source of bugs. Alibaba OpenSource and Alibaba Cloud Int. look alike but are separate endpoints, each with its own price list and policy, and deepinfra differs from deepinfra/turbo in the same way. In a routing config, a bare provider slug covers that provider's ordinary and regional variants, while a slug with a suffix targets exactly one endpoint. Service tier endpoints are the documented exception: openai/fast and google-vertex/flex never match a bare slug, and you reach them only by passing service_tier or using a tier-suffixed slug. That detail returns later alongside :floor and flex.
Keeping customer identity out of requests
A customer's name or email never has to appear in an API call. The optional user field (safety_identifier on the Responses API) accepts a pseudonymous ID. If an upstream provider demands an identity, OpenRouter hashes the value and sends the hash instead of the original. Without the field, all requests present one shared account-level identity upstream, so a provider block caused by a single end user could affect the whole account.
The field also has a routing side effect. A stable identifier makes caching sticky: the same user keeps landing on the same provider so the cache stays warm, and different users spread across providers. That helps cost and throughput, and it's a reminder that the identifier does real linking work.
So is any of this anonymous? You can avoid naming the end user to the host. You can't assume the conversation itself is anonymous. OpenRouter knows which account sent the request, and both OpenRouter and the chosen host process the prompt. If the prompt contains someone's email, customer record or signature, hashing a separate user ID hides none of it. Strip unnecessary personal data inside your application before anything is sent.
Four privacy controls in provider routing
onlylimits a request to an allowlist of hosts. Account-wide allowed providers act as a ceiling, and the per-request list narrows within it. If nothing satisfies both, the call fails with a 404, so the app has to handle that.data_collection: "deny"sends traffic only to providers that don't collect user data for training, and it can also be set account-wide. Don't treat it as a replacement forzdr: true. OpenRouter's ZDR notes point out endpoints that never train on your data yet still keep it for abuse detection or legal reasons. Training and retention are different promises, controlled separately.zdr: truekeeps routing on Zero Data Retention endpoints. At request level it combines with your account and guardrail settings as an OR, so it can tighten enforcement and never loosen it.- Regional routing for the EU and US exists on enterprise accounts, for cases where data residency matters more than retention.
ZDR has two limits that people overlook. For one, it governs which providers handle inference. Plugins and tools you turn on, web search for example, sit outside it. For another, OpenRouter counts implicit in-memory prompt caching as not retaining anything, so an endpoint that caches implicitly stays eligible under ZDR. Explicit caching, with control headers set by you, is excluded. That's a label OpenRouter applies for ZDR purposes and doesn't prove nothing gets stored. The logic is almost the reverse: explicit caching openly stores data for reuse.
OpenRouter also handles requests apart from the host's policy. Prompt and response logging is off by default. Request metadata is still kept, though, and a small sample of prompts gets categorized by a zero-data-retention model for reporting and rankings, stored anonymously for anyone who hasn't joined the input/output program.
What the same model costs on different hosts
A single model is often offered by several hosts, each at its own rate. Before the figures, some context that should calm things down. OpenRouter's default routing already weighs price when balancing load. Among providers that haven't had an outage lately, selection probability scales with one over the price squared. A host at $1 therefore gets picked nine times as often as one at $3, and defaults won't steer you to the most expensive endpoint by habit.
The spread in the table is the range you could face if routing is left implicit, retries shuffle you around, or an allowlist pins you somewhere expensive. Treat it as a bound, not a forecast.
The figures come from OpenRouter's public provider tables and endpoint API, checked for four models on 8 September 2026 at roughly 07:07 UTC. To compare token charges evenly, each request was modeled as 10,000 uncached input tokens plus 2,000 billable output tokens, multiplied by 1,000 requests. Each row shows the lowest and highest cost among the endpoints returned. It doesn't promise that every endpoint will be available or suit your application.
All amounts are in USD. They leave out taxes, credit-purchase fees, cache charges, retries and optional tools, and they use the currently displayed rates, promotions included. Output budgets count any reasoning tokens billed as output. The same token budget doesn't guarantee the same responses, quality, speed or quantization across hosts.
A warning goes with one of the rows. Alibaba's Qwen3 Coder price depends on context length, with two steps up instead of one. The endpoint API showed base rates of $0.975 input and $4.875 output per million tokens at the snapshot. From 32,000 input tokens the rates were $1.755 and $8.775, and from 128,000 they were $2.925 and $14.625. The model page displays just part of that, so read the pricing overrides in the endpoint API. The table uses the base rate, which a 10,000-token request pays. Check which tier your real context falls into before borrowing any number.
The calculation is easy to repeat:
request_cost = (10,000 × input_rate_per_million
+ 2,000 × output_rate_per_million) / 1,000,000
spread_percent = (highest_request_cost / lowest_request_cost - 1) × 100
To pull current numbers, request a model's endpoint list from https://openrouter.ai/api/v1/models/{author}/{slug}/endpoints. The API reference marks this route as authenticated, so pass your key as a bearer token. The API quotes prices per token, so multiply by a million to line up with the table. Look at pricing overrides for your context length too, and take care not to count a listed discount twice.
"Cheapest" depends on the whole request
Even "cheapest provider" can mislead. Google Vertex listed Qwen3 Coder input at $0.22 per million, undercutting DeepInfra at $0.30, yet its output price was $1.80 against DeepInfra's $1.00. With this workload, DeepInfra ended up cheaper in total. Add up the full request instead of trusting the first number shown.
Promotions need the same care. At the snapshot, GMICloud's DeepSeek rates included a 28% discount. Budgeting around a temporary offer assumes it will still exist. Prices, discounts and available hosts can change, and a single snapshot can't show how often.
Price isn't the only measure either. In the snapshot, the lowest-cost DeepSeek V3.2 endpoint produced 3 tokens per second. The lowest-cost Qwen3 Coder endpoint managed 10 tokens per second at a 5.84 second median latency, and a host charging 16% more ran three times faster. Batch jobs should take that deal happily. Anything with a person waiting shouldn't.
Sorting by price, on purpose
When cost leads, choose provider.sort: "price" on purpose. It has two effects: providers get tried cheapest first, and load balancing is turned off. You give up the uptime-friendly spread for a cost order you can predict, so weigh it as a genuine trade. A fallback goes to the next cheapest eligible endpoint, never to some random pricey one.
A shorthand exists too: tack :floor onto the model slug and the request sorts by price while also admitting flex service tier endpoints, which makes it a wider version of sort: "price".
An illustrative provider block for a Chat Completions request:
{
"provider": {
"sort": "price",
"zdr": true,
"data_collection": "deny",
"max_price": {
"prompt": 1,
"completion": 2
}
}
}
In that example, the limits are one dollar per million tokens going in and two dollars per million tokens coming out. They aren't a ceiling on the cost of a whole request, so choose values for your own workload. Performance preferences never block a call, but max_price can leave zero eligible providers and halt the request, so the application needs a plan for that case.
Two fields were left out on purpose.
allow_fallbacksistrueby default, which makes writingtruepointless. Onlyfalsechanges behavior, and it works by itself as well as alongside a list. Three roles are worth keeping apart.onlyis a strict allowlist already, so the router stays inside it with no help.orderexpresses a preference only, and once your list runs out the router may carry on to other providers unless fallbacks are off.allow_fallbacks: falseends the request when the chosen endpoint fails instead of trying anyone else.require_parameters: trueconfines routing to providers that handle every parameter you send. It protects correctness and fights the cost goal, since it can eliminate the cheapest hosts. Turn it on only when a request really depends on it. With it off,tools,response_formatand verbosity still work as soft preferences between hosts serving the same model.
Default to price ordering, and switch only when latency, throughput or a vetted host counts for more. Keep in mind that sorting can neither lock in a promotional rate nor promise the smallest final bill at your exact output length.
A prompt for auditing your own integration
Run the following in Claude Code or any assistant that can read the repository. In a plain chat window, attach the integration files first.
Audit this application's OpenRouter integration and implement justified improvements.
Trace every LLM request path, including SDK wrappers, agents, background
jobs, streaming, retries and model/provider fallbacks. Show the files and
the effective routing configuration sent over the wire.
Check current official OpenRouter documentation and live endpoint prices.
Do not rely on remembered API syntax or the cheapest advertised input rate.
Compare input/output costs for representative workloads, including caches,
reasoning tokens, context tiers, discounts and retries.
Explain the current selection policy and why it fits, or fails, the workload.
For cost-led calls, use explicit price sorting unless an existing latency,
throughput, quality or reliability requirement justifies another policy.
Preserve valid existing policies. Do not silently switch models or relax
privacy, region, provider allowlist or capability requirements to save money.
Check whether wrappers actually transmit provider settings. Add configurable
price ceilings and bounded fallback/retry behaviour where appropriate.
If business limits are unknown, expose the configuration and flag the missing
decision; do not invent production budgets. Handle no-eligible-provider
errors without retrying through an unrestricted route.
Inspect what personal data, user identifiers and prompt content leave the
application. Prefer pseudonymous IDs and minimise unnecessary personal data.
Identify OpenRouter logging/account settings that require dashboard review;
do not claim to have verified settings you cannot access.
Record model, actual provider, usage, billed cost when available, latency
and failures without logging prompt content or secrets by default.
Add focused tests for outgoing settings, streaming and failure paths.
Make the smallest justified patch. Report the original behaviour, changes,
tests, remaining decisions and sources. Do not deploy.
If repository access is unavailable, request the necessary files and provide
a proposed diff; do not claim changes were made.