← All posts
Product

One API Key Across Providers: The Integration You Stop Writing

Provider breadth behind a single OpenAI-compatible key. What the swap changes in your codebase, what the model string buys you, which providers are live today, and the integration work that stops being yours to maintain.

nRouter team · 11 min read
One API Key Across Providers: The Integration You Stop Writing

What it does

One nRouter key, pointed at https://api.nrouter.ai/v1, reaches every provider in your live catalog through the OpenAI request and response shape you already write. Choosing a different provider is a different value in the model field. You do not hold a provider key, a provider SDK, or a provider invoice.

The one-paragraph version: nRouter is an OpenAI-compatible endpoint sitting in front of multiple model providers. Your application keeps the OpenAI SDK it already imports, keeps its streaming loop, keeps its tool-calling code, and changes two constructor arguments. In exchange, the per-provider integration layer — the auth scheme, the request translation, the response normalisation, the key rotation, the second billing portal — stops being code you own.

This post is about that one property: breadth behind one credential. It is not about picking the cheapest model for a given call (that is routing as a cost lever), not about capping spend (budget ceilings), and not about splitting the bill across your own customers (per-customer billing). Those three sit on top of this one. This is the seam they all need first.

The job it does for you

Here is the workflow most teams are actually running, written honestly.

Before. You start with one provider because one provider was enough. Six weeks later somebody wants a second one — a cheaper model for classification, or a longer context window, or simply a hedge against a bad afternoon. Adding it means a new SDK in requirements.txt, a new secret in your secret manager with its own rotation schedule, a new client wrapper because the two SDKs disagree about what a message looks like, a new error taxonomy because the two providers disagree about what a rate limit looks like, and a new invoice that arrives on a different day of the month in a different format. None of that is hard. All of it is permanent. And because it is permanent, the third provider is a conversation rather than a change, which is how teams end up with exactly one provider and a strong opinion that they are multi-model.

The tell is the in-house abstraction layer. Almost every team past the second provider has one: a LLMClient interface, two or three concrete implementations, a factory, and a translation function that is one edge case behind whatever the providers shipped last week. It works. It is also a piece of infrastructure your team now maintains forever, whose entire job is to make three APIs look like one API — a problem that was solved by the first of the three APIs becoming the de facto standard.

The disagreements are individually trivial and collectively endless. Anthropic's Messages API takes the system prompt as a top-level system parameter and reports consumption as usage.input_tokens and usage.output_tokens; the OpenAI shape carries the system prompt as the first entry of messages and reports usage.prompt_tokens and usage.completion_tokens. Neither design is wrong. Reconciling them is simply a piece of code you now own, and it has to be right on the day a provider adds a field.

After. There is one client, one credential, one error taxonomy, one set of response headers, and one bill. The model is a string. The abstraction layer is deleted, and the thing it was abstracting is now a configuration surface rather than a code surface.

The honest version of the payoff is not "it is faster." It is that the marginal cost of considering a new model drops to roughly zero, and that changes which decisions get made. A model that would need two sprints to evaluate does not get evaluated. A model that needs a string change gets evaluated on a Tuesday afternoon.

What the catalog actually contains today

The catalog is a live thing, and this is the one place a blog post ages badly, so the answer here is a pointer rather than a list: the models page is authoritative for what your key can reach right now, and the API returns the same list programmatically. Anthropic, OpenAI and AWS Bedrock are live today. Anything else you see on that page is live too — the page is generated from what the gateway actually serves, not from a marketing table.

Two things worth knowing about how that list behaves:

  • It is per-environment. What your key reaches is what your organisation is entitled to, which is why the endpoint is the right source and a hardcoded list in your code is not.
  • A model that prices at nothing is not enabled. If a model appears, it has a rate. If its cost cannot be determined for a specific call, the call is reported as unpriced rather than as $0 — the reasoning is in cost honesty, and it matters here because a breadth claim that quietly includes unbillable models is not a breadth claim.

You do not bring provider keys. nRouter holds the provider relationships and you hold credits and one nRouter key. That is a real trade with real consequences in both directions, argued in full in why we do not do BYOK, and it is the reason the integration collapses to one credential at all.

How it works

The customer-visible contract is small enough to state completely.

What you configure. A base URL and a key. Optionally, a router alias if you want a name that resolves to a set of models rather than to one model.

What the gateway does. It authenticates the key, resolves the model, translates your OpenAI-shaped request into whatever the target provider expects, calls it, translates the response back, and records the call against your organisation with its settled cost.

What you get back. The standard OpenAI response body, plus a set of x-nr-* headers that describe what actually happened:

HeaderWhat it tells you
x-nr-routed-modelWhich model actually served the request
x-nr-model-fallbackPresent when failover occurred, as requested->served
x-nr-request-costThe settled cost of this call — absent when the cost is not knowable
x-nr-cost-statusexact or unpriced, so an absent cost is never ambiguous
x-nr-request-idThe identifier to quote when correlating logs

Those headers are the reason breadth does not cost you observability. With one provider you could read the provider's own dashboard. With five you would be reconciling five dashboards, which is why most multi-provider teams end up with worse cost visibility than single-provider teams. Here the visibility is on the response, in one shape, regardless of who served it.

The last piece is failover, and it is worth stating precisely because it is easy to over-claim. If the model you asked for fails to deliver, the request is retried on an equivalent model from a different provider and the header tells you it happened. You can replace that default with your own ordered chain. What the chain cannot do is fire selectively — a model you list is attempted on any delivery failure, not only on the failure type you had in mind. Provider fallback chains covers the design, including the trap that a retry is a second call and therefore a second bill.

Set it up

The whole migration is two constructor arguments. Here is the diff against a standard OpenAI integration:

  from openai import OpenAI

- client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
+ client = OpenAI(
+     base_url="https://api.nrouter.ai/v1",
+     api_key=os.environ["NROUTER_API_KEY"],
+ )

Everything downstream of that line is unchanged. To read the headers alongside the body, use the SDK's raw-response accessor rather than reaching for a second HTTP client — with_raw_response is documented in the OpenAI Python SDK and works unchanged against this base URL:

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.nrouter.ai/v1",
    api_key=os.environ["NROUTER_API_KEY"],
)

raw = client.chat.completions.with_raw_response.create(
    model="claude-sonnet-4-5",                 # any id from /models
    messages=[{"role": "user", "content": "Summarise this contract in 3 bullets."}],
)

completion = raw.parse()
print(completion.choices[0].message.content)
print(raw.headers.get("x-nr-routed-model"))
print(raw.headers.get("x-nr-request-cost"))    # absent when unpriced
print(raw.headers.get("x-nr-cost-status"))     # "exact" | "unpriced"

Ask the endpoint what it can reach rather than trusting a list you wrote down:

curl -s https://api.nrouter.ai/v1/models \
  -H "Authorization: Bearer $NROUTER_API_KEY"

The same swap works from every client that speaks the OpenAI protocol, which in practice means most of the framework ecosystem: the Python SDK and Node SDK pages carry the exact constructor for each, and the framework pages cover the wiring for LangChain, LlamaIndex and Vercel AI. If you are coming from another gateway rather than from a provider directly, migrating off OpenRouter is the same shape of change with a few extra notes on header parity.

Adding a model that shipped this morning

The claim "adopting a new model is a string change" is true in a narrow, useful sense and false in a broad one, and it is worth separating the two.

True: the plumbing is a string. No dependency, no credential, no client, no deploy of a new integration path. Change the model value, or change what a router alias points at in Router Settings and change nothing in code at all.

Not true: the evaluation is not a string. A new model has a different tokeniser, different latency, different refusal behaviour, and different failure modes on your particular prompts. Swapping it in blind is how a quiet quality regression ships. The right sequence is a deterministic split of real traffic between the incumbent and the candidate, with a quality signal you decided on before you looked — deterministic A/B testing across model variants is the mechanics.

What the single seam buys you is that step one costs nothing, so step two actually happens.

Worked example with numbers

This is an integration-effort example, and the engineering rates are yours to substitute — the arithmetic is what generalises, not the figures.

Take a team that wants three providers in production: a flagship for reasoning, a small model for classification, and a third as a hedge. Estimate the per-provider integration at three engineer-days of first build (SDK, auth, translation, error mapping, tests) and one engineer-day per quarter of maintenance (SDK bumps, deprecations, a changed error shape).

LineDirect integrationsOne key
First build, 3 providers9 engineer-days0.5 engineer-days
Maintenance, 4 quarters12 engineer-days0
Credentials to rotate31
Billing relationships to reconcile31
Cost visibility3 dashboards, 3 formats1 header, on every response
Year-one engineering21 engineer-days0.5 engineer-days

Illustrative effort figures, not a measured platform claim. Substitute your own build and maintenance estimates.

The line that usually decides it is not the 21 days. It is row three and row four. Three provider credentials means three rotation calendars and three chances to be the team that discovers an expired key in production. Reducing that to one nRouter credential is a security simplification before it is a convenience — and the one credential you keep is itself divisible into scoped, revocable keys per service, which is the subject of virtual keys vs master key.

What it costs

The breadth is not a paid feature, and neither is anything else: the single line a plan moves is the platform fee — 4% on top of provider spend on pay as you go, 0% on Pro at $50/mo or $500/yr — and the current table is on the pricing page.

The crossover is arithmetic you can do without a call: the 4% fee reaches $50 at around $1,250/month of provider spend on the monthly plan, and around $1,042/month on the annual one. Below that, pay as you go is cheaper. The full walkthrough, including when the annual commitment is the wrong answer, is in from credits to Pro.

Two things to be plain about. There is no free tier here: the card comes before the first call, the $5 minimum is a real charge with the platform fee on top, and the only offset is a $10 first-purchase bonus — load $5, get $15, once per organisation, after the payment clears. And because the fee sits on top of a public provider rate, a team that has negotiated committed-spend pricing directly with a provider will not carry that rate through here. For those teams the arithmetic can come out the other way, and it should be run before anything else.

Where it fits with the rest of the platform

Breadth behind one key is the substrate. The capabilities worth having are the ones that become possible once every call passes through one place:

Limits and what it will not do

An unqualified capability post is marketing, so here is the qualified version.

  1. It does not give you every model in the world. It gives you your live catalog. The models page is the answer, and if a model you need is not on it, the honest answer today is that it is not available through us today.
  2. It does not let you bring your own provider key. No BYOK. If you have a negotiated rate with a provider, you cannot carry it through this seam.
  3. It adds a hop. Your availability now includes ours. That is why the status page is public and why failover behaviour is documented rather than implied.
  4. Compatibility is the OpenAI protocol, not every provider's private extensions. A parameter that exists only in one vendor's own SDK, with no OpenAI-shaped equivalent, is not reachable by definition.
  5. A fallback chain cannot be scoped by failure type. A model you list will be attempted on any delivery failure — 5xx, network error, 429 and context-window-exceeded alike. Design the chain knowing that.
  6. SOC 2 Type II is in progress, not certified. The trust page states the current posture; if you ever see us write "certified" before it is true, that is the sentence to hold us to.

Try it

Create an account at signup, load the $5 minimum (a first purchase is matched with a $10 bonus, so $5 becomes $15 in credits), and point one existing service at https://api.nrouter.ai/v1 with NROUTER_API_KEY. Then do the thing that actually proves the claim: call two different providers from the same client in the same afternoon, and read x-nr-routed-model off both. The quick start is ten minutes end to end, and the chat-completions reference is the full request shape.

See also

Sources

Verified 2026-08-23. If any linked page has changed since, email hello@nrouter.ai and we will correct this post.

OpenAI, Anthropic and AWS are trademarks of their respective owners. nRouter is not affiliated with or endorsed by them. All claims above are sourced from their public pricing or documentation on the date shown.

Share
Written by nRouter teamEngineering, product, and company posts from the nRouter team — code-first, cost-honest, no vendor-marketing fluff.