Opens in a new tab
Guide

What is OpenRouter? A practical guide to choosing and using AI models

In brief

Understand the model marketplace and gateway, compare chat and API workflows, and choose a useful route through its models, providers, costs and privacy controls.

18 min read

Sources
Overhead view of an orange locomotive at a three-way railway junction, a metaphor for model routing.
AI-generated conceptual illustration / AIOpsSRE
On this page12 sections

The first model in an application usually gets a direct connection. Then someone wants to try another one. Now there is another account, another key, a slightly different request format, and a second bill to explain. Model comparison has acquired a small integration project.

OpenRouter is a hosted gateway and marketplace for AI models. It gives you one account and API through which you can use models from different developers and hosting providers. You can also try models in its web chat. OpenRouter handles the connection to an eligible provider; that provider runs the model and returns its answer. You do not install OpenRouter on a GPU or download the models to your laptop.

That makes it useful for exploring models, connecting a model-capable app, or building software that needs more than one model service. It does not make every model equally capable, every provider interchangeable, or every route suitable for your data. The practical advantage is being able to change your choice without rebuilding all the plumbing around it.

This guide starts with what OpenRouter is and when it earns its place, then follows the two common ways to use it: trying a task in chat and connecting an application. For a first integration, start with one model and approved providers. Check answer quality, privacy and cost before adding fallback models. A large catalog is a reason to experiment carefully, not a requirement to use all of it.

How a model marketplace became a gateway

OpenRouter began in early 2023, during the first wave of enthusiasm around ChatGPT and competing language models. Its cofounders were Alex Atallah and Louis Vichy. Atallah, its cofounder and CEO, had previously co-founded OpenSea, as his own biography records. The useful idea was that developers would want access to a changing collection of models rather than make one permanent choice about which company's intelligence their software would use.

There are two parts to that idea. A marketplace helps you discover models and compare their characteristics. A gateway gives your application a common entry point to actually use them. OpenRouter combines those jobs. Finding an interesting model and connecting it can happen through the same service, with common usage accounting instead of another entirely separate integration.

The company raised $40 million across seed and Series A rounds, described in a June 30, 2025 announcement by its legal adviser, Orrick. On August 19, 2026, Stripe announced an agreement to acquire OpenRouter. OpenRouter said it would retain its name, product and roadmap. That announcement was subject to closing conditions; it should not be read as proof here that the transaction has closed.

The history explains the product better than a model count does. OpenRouter's own account of its founding centers on a multi-model future. Its bet is that model choice will remain useful as capabilities, prices and providers change. Whether you need that choice today is a separate question.

The model, its provider, and the router are different things

Suppose you choose Meta's Llama model. Meta developed the model, but a different company may operate the servers that answer your request. OpenRouter can sit in front of that company and forward the request to it. There are three distinct roles:

  • The model generates the answer. Its capabilities determine whether it can write, reason, interpret an image, or use the features your task needs.
  • The provider runs a model endpoint, the service address that accepts requests. It contributes serving capacity, latency, pricing and data-handling terms.
  • OpenRouter presents the common API and selects an eligible route under the applicable settings.

Those roles can overlap in a company, but they remain useful distinctions. Selecting a model does not necessarily select one hosting company. Selecting a hosting company does not tell you which model will do the work. A model may also be available through several endpoints with different characteristics. The provider-routing documentation describes how those choices are controlled.

There are also two ways to change course. Provider fallback tries another eligible provider serving the same model. Model fallback tries a different model after an error. The second change can alter the answer's behavior as well as its availability, so the backup model needs its own evaluation.

Model A routes among its eligible providers; a configured model fallback moves to Model B and its own eligible providers.
Provider fallback changes the serving endpoint; model fallback changes the model. This conceptual illustration does not show an observed run.

A third option, Auto Router, delegates model selection to a router. That is a different experiment from explicitly choosing one model or listing backups. It can be useful when you want automated selection, but it makes a poor starting point if you are still trying to understand why two answers differ. First learn what one selected setup does with your task; then decide how much selection to automate.

When OpenRouter saves work, and when it adds it

OpenRouter is especially appealing when model choice is part of the work. A developer can compare a lower-cost summarizer with a more capable model without writing a separate client for each. Someone using a model-configurable writing or coding app can change the backend through one account. A platform team can give several applications a shared way to obtain models while keeping provider restrictions and spending visible.

The everyday version is less elaborate. You might compare two models on a difficult translation, turn public release notes into a customer-friendly explanation, or ask different models to critique a draft. The point of comparison is a concrete difference you can judge: did the translation preserve the ambiguity, did the explanation retain the exception, or did the critique identify something useful? Collecting three agreeable answers is not much of an evaluation.

OpenRouter is less compelling if one provider already does everything you need and your integration is working. A direct API keeps the request path shorter and the provider relationship more direct. If you only want an assistant to help you write and organize your day, a finished assistant app may be the simpler product. Model access supplies the engine; it does not automatically supply the entire application experience.

Your main needA reasonable starting pointWhat you take on
Try several hosted models through one accountOpenRouter chat or APIModel and provider choices, usage charges, and another service handling requests
Use one provider's capabilities directlyThat provider's APIA separate integration and account for each additional provider
Operate a common gateway under your own controlA self-hosted gateway such as LiteLLMDeployment, updates, availability and the underlying provider connections
Run inference on hardware you controlA local model runtimeHardware capacity, model selection and serving performance
Get a ready-made personal assistantAn assistant applicationThe choices and workflow that application exposes

LiteLLM, for example, offers an SDK and a self-hosted proxy with a common model interface. It can be a good fit when operating the gateway is intentional. Self-hosting the gateway alone does not make inference local: if it calls a remote provider, your prompt still goes there. For the distinction between running a model locally and calling one over an API, see the inference guide.

OpenRouter can simplify provider integration while becoming a dependency of its own. Provider failover may help when a serving endpoint fails; it does not remove the need to consider the gateway's availability. That is a tradeoff to make consciously, particularly if the application already has a reliable direct route.

Choose around the job, not the leaderboard

The model catalog is a good discovery tool. It lists model IDs and characteristics such as supported input types, context size and prices. Context size is the amount of input and generated material a model can accommodate within its applicable limits. A larger number gives you more room; it does not promise that the model will use every part of a long document well.

Start with the task's minimum requirements. A short email rewrite needs reliable text generation and good instruction following. A document comparison needs enough context for the supplied material. An image question needs an image-capable model. An application that expects tool calls or structured data needs compatible support from the selected model and endpoint. A familiar model name is not a substitute for checking those requirements.

Then compare quality and total effort on a few representative inputs. Keep the instructions and source material unchanged between candidates. Inspect the result before looking at the price: a cheap answer that drops the one exception you cared about has already failed the task. Once several candidates do acceptable work, cost and responsiveness become useful differentiators.

Keep the experiment understandable. If you change the model, provider, prompt and context at the same time, you may like the new answer without knowing what improved it. For repeatable work, record the model identifier, prompt version and returned route rather than relying on a screenshot of whichever model looked good yesterday. The models API is available when you later need to collect current metadata programmatically.

Try a real task in chat before building a client

OpenRouter's web chat is the low-friction way to explore a model. Create or sign in to your account, review its privacy and credit settings, select a model, and begin with non-sensitive material. Check the selected model's current price before sending; a chat interface does not make the underlying inference free.

Here is a small constructed exercise you can judge without being an expert in the topic. Ask a model to rewrite this notice for customers:

Rewrite this notice in plain English, under 70 words.
Keep the time zone, the maximum interruption, and the exception.
Do not imply that all customers will lose access.

On Tuesday at 14:00 UTC, reporting will undergo maintenance.
Some customers may be unable to load new reports for up to 10 minutes.
Previously exported reports remain available.

Read the result for the three things that make the notice useful: 14:00 UTC, an interruption of up to 10 minutes affecting some customers, and continued access to exported reports. A cheerful rewrite that says “reporting will be unavailable” has made the message easier to read by making it less accurate. That is a fairly expensive editing habit, even when the tokens are cheap.

Try another model with exactly the same request. Compare what you would actually send after review, including the edits you had to make. This exercise tells you something about these models on this kind of instruction. It does not rank them for programming, research or every language. Good evaluation starts with resisting the temptation to generalize from the first answer you liked.

If this is all you need, you can stop at chat. If you want the same operation inside a script, an app or a repeated workflow, the API is the next step. Apps with explicit OpenRouter support normally ask for its API key and a model choice; otherwise confirm that the app supports the relevant compatible API and routing options rather than assuming any model selector will work.

What you pay for when the model answers

OpenRouter's ordinary credit workflow charges for model usage. Input and output often have different prices, so the length of the answer alone does not describe the bill. The instructions, previous conversation and documents sent to the model can all contribute input. Depending on the model and feature, reasoning, images, caching, tools or per-request charges can also matter. The billing FAQ explains the categories and the separate fees associated with purchasing credits and BYOK, or bringing your own provider keys.

For a hypothetical text request with 2,000 input tokens at $1 per million and 500 output tokens at $2 per million, the token subtotal is $0.003. Ten thousand identical requests would be $30 before other applicable charges. Those are teaching rates, not a quote for a named model. The calculation is straightforward; knowing which requests and billed categories belong in it is the interesting part.

A long chat can send more context on later turns. An application may retry, ask for revisions or make several calls for one visible answer. Count that work when you compare approaches. BYOK also leaves you with the underlying provider's bill and terms; it should not be mistaken for free inference through a different login.

For a small trial, set a limited budget and inspect Activity after the first call. For a team, distinguish the pooled workspace budget from limits applied to members or keys. OpenRouter's spend-control guide explains those layers. A lower token price and a spending limit solve different problems: one reduces the rate you pay, while the other helps contain how much work can accumulate.

Which companies get the prompt?

With a hosted route, the prompt passes through OpenRouter and an eligible provider. Check that route before sending customer records, private documents or production logs. Removing a name from a log line may leave an account number, access token or other identifying detail elsewhere in the same bundle. Begin with synthetic or public material while you establish what the application actually sends.

Provider policies distinguish training from retention. A service can promise not to train on your inputs while retaining them for another reason. OpenRouter documents provider policy information and lets you restrict routes using controls including data_collection: "deny" and zdr: true. The latter requires endpoints classified as zero data retention. Read the provider policy explanation and ZDR scope; a policy for one endpoint need not describe every endpoint offered by the same company.

OpenRouter's own settings matter too. Its data-collection documentation describes separate opt-ins for prompt/output logging and product improvement, request metadata retention, and anonymous prompt categorization. Your application's logs are another possible copy. ZDR is not a reason to stop tracing where the data travels.

Tools and plugins can introduce additional recipients, and inference-routing restrictions do not automatically cover all of them. Geography is also separate from retention. If processing must stay in a particular region, review the regional service and applicable terms. A provider's headquarters tells you remarkably little about the machine that answered your request.

Connect one model to a small Python client

The following example turns incident observations into a handoff draft. It gives the API something useful to do while keeping the source short enough to inspect yourself. The same client structure could summarize public release notes or rewrite a notice; the instruction and input are what make this particular request an incident handoff.

You need Python 3, an OpenRouter account, an API key you create yourself, and enough approved credit for the selected route. The client uses Python's standard library, so no SDK installation is required. Keep the key on the machine running it, outside source control and browser-delivered code.

  1. Choose a text-generation model from the catalog and copy its exact ID. For this short exercise, a non-reasoning model keeps the completion allowance easier to interpret.
  2. Inspect the available providers and select the ones approved for your input. A base provider slug can match multiple endpoint variants; use the full endpoint slug when approval is narrower.
  3. Check current prices and policy compatibility. This example requires ZDR and permits at most $1 per million input tokens and $2 per million output tokens. Those deliberately limited settings may leave no eligible route. Choose a suitable combination or revise the limits intentionally before sending.
  4. Set the experiment's account or key budget, then inspect the request in dry-run mode.

The model and provider placeholders below are intentional. They are choices to verify against the current catalog and your requirements, rather than a claim that one named pairing will always be available. OpenRouter's quickstart gives the basic connection pattern; the example adds a visible route restriction and failure handling.

A handoff with recovery still unconfirmed

Our constructed incident begins with a checkout release at 14:02 UTC. Timeouts rise at 14:06. Connection-pool waiting increases while database CPU remains at 35%, and a responder begins rollback at 14:11. There are no post-rollback measurements. The model has enough to organize a handoff, but not enough to announce recovery.

Constructed checkout timeline ends at rollback start, with no recovery measurement supplied.
The handoff should retain the gap after 14:11. Starting rollback does not supply evidence that checkout recovered.

The important part of the request is shown below. This explanatory excerpt omits the full message objects; copy the complete client from the expandable section below into handoff.py when you are ready to run it. The client prints the proposed request by default. Only --send enables the network call and asks for the key.

{
  "model": "YOUR_MODEL_ID",
  "messages": [{"role": "user", "content": "..."}],
  "max_completion_tokens": 500,
  "provider": {
    "only": ["YOUR_PROVIDER_SLUG"],
    "allow_fallbacks": true,
    "require_parameters": true,
    "data_collection": "deny",
    "zdr": true,
    "sort": "price",
    "max_price": {"prompt": 1, "completion": 2}
  }
}

First run it without contacting OpenRouter:

python3 handoff.py --model YOUR_MODEL_ID --provider YOUR_PROVIDER_SLUG

Check the JSON for your selected model, provider restriction and synthetic observations. When you are ready to incur the call's charge, add --send in an interactive terminal. The client stops if hidden key input is unavailable. Add another --provider argument only for another approved provider.

The client sends a non-streaming request to /api/v1/chat/completions, then prints available generation metadata before deciding whether the text is usable. Its max_completion_tokens allowance is 500. On most providers, reasoning and visible output share that allowance, so a reasoning model may exhaust it before producing a useful handoff. Review the selected model before increasing the budget or changing reasoning settings. The request reference and reasoning guide explain those fields.

A token-limit stop, empty answer or malformed response is rejected. The client makes no automatic retries, and its 45-second socket timeout limits a blocked network operation rather than establishing a complete production deadline. The local tests exercise those failure paths without making model calls.

Full Python client: copy into handoff.py
"""Synthetic teaching example. Dry-run unless --send is specified."""
import argparse
import getpass
import json
import socket
import time
import urllib.error
import urllib.request
import warnings

INCIDENT = """Synthetic incident, all times UTC:
14:02 checkout release r42 finished.
14:06 checkout timeout rate rose from 0.2% to 7%.
14:08 database CPU remained at 35%; connection-pool wait rose.
14:11 responder began rollback. No post-rollback metrics supplied.
No customer count, traces, or root-cause analysis supplied.
"""

def make_request(model, providers):
    return {
        "model": model,
        "stream": False,
        "max_completion_tokens": 500,
        "messages": [
            {"role": "system", "content": (
                "Write an incident handoff in three sections: Observed facts, "
                "Uncertainty, Next checks. Use only the supplied evidence. "
                "Keep timestamps. Do not claim a root cause or recovery "
                "without evidence. Do not execute or suggest automatic changes."
            )},
            {"role": "user", "content": INCIDENT},
        ],
        "provider": {
            "only": providers,
            "allow_fallbacks": True,
            "require_parameters": True,
            "data_collection": "deny",
            "zdr": True,
            "sort": "price",
            "max_price": {"prompt": 1, "completion": 2},
        },
    }

def extract_answer(data):
    if not isinstance(data, dict):
        raise ValueError("Expected a response object")
    if data.get("error"):
        raise ValueError("API returned an error object")
    choices = data.get("choices")
    if not isinstance(choices, list) or not choices:
        raise ValueError("Missing choices")
    choice = choices[0]
    if not isinstance(choice, dict) or not isinstance(choice.get("message"), dict):
        raise ValueError("Malformed choice")
    if choice.get("finish_reason") != "stop":
        raise ValueError("Incomplete or unexpected finish; review before use")
    content = choice["message"].get("content")
    if not isinstance(content, str) or not content.strip():
        raise ValueError("No usable text returned")
    return content

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--model", required=True)
    parser.add_argument("--provider", action="append", required=True)
    parser.add_argument("--send", action="store_true")
    args = parser.parse_args()
    body = make_request(args.model, args.provider)
    if not args.send:
        print(json.dumps(body, indent=2))
        return
    try:
        with warnings.catch_warnings():
            warnings.simplefilter("error", getpass.GetPassWarning)
            key = getpass.getpass("OpenRouter API key (hidden): ")
    except getpass.GetPassWarning:
        raise SystemExit("Hidden input unavailable; use an interactive terminal")
    request = urllib.request.Request(
        "https://openrouter.ai/api/v1/chat/completions",
        data=json.dumps(body).encode(),
        headers={"Authorization": "Bearer " + key,
                 "Content-Type": "application/json"},
        method="POST",
    )
    started = time.monotonic()
    try:
        with urllib.request.urlopen(request, timeout=45) as response:
            data = json.load(response)
    except urllib.error.HTTPError as error:
        raise SystemExit(f"HTTP {error.code}; inspect Activity before retrying")
    except (urllib.error.URLError, socket.timeout, TimeoutError):
        raise SystemExit("Transport failure; remote completion is unknown")
    except (ValueError, KeyError, IndexError, TypeError):
        raise SystemExit("Unusable response; inspect Activity before retrying")
    if not isinstance(data, dict):
        raise SystemExit("Malformed response; inspect Activity before retrying")
    print(json.dumps({"id": data.get("id"), "model": data.get("model"),
                      "usage": data.get("usage"),
                      "elapsed_seconds": round(time.monotonic()-started, 2)},
                     indent=2))
    try:
        answer = extract_answer(data)
    except ValueError as error:
        raise SystemExit(str(error) + "; metadata retained above")
    print("\nDRAFT FOR HUMAN REVIEW\n" + answer)

if __name__ == "__main__":
    main()

Read the settings as a policy for this request

only admits the selected providers. order would express a preference that can extend to other providers if fallbacks are allowed. Here, fallbacks remain enabled within the allowlist, sort: "price" prefers cheaper eligible endpoints, and require_parameters filters for parameter support.

max_price sets token-rate ceilings in dollars per million tokens, not a total task budget. Combined restrictions can leave no eligible route. An error in that case can mean the settings are doing their job; inspect the combination before weakening a privacy requirement. These controls are described in the routing reference.

The application still needs something sensible to do when its approved route is unavailable. For this handoff, showing the original observations is useful. The responder can continue without asking a different company to process the incident data merely to fill an empty output box.

Check the handoff and find out who produced it

A useful draft preserves the timestamps and distinguishes the observed timeout increase from a suspected cause. It might propose checking post-rollback timeouts, connection-pool waiting and traces showing where requests spend time. It should leave recovery unconfirmed. Those are review criteria for this constructed input, not a transcript of a model result.

Reject a draft that turns steady database CPU into proof the database was healthy, invents a customer count, or treats the start of rollback as the end of the incident. Polished prose can make those leaps unusually easy to miss. Keep the five observations beside the answer and check the claims before using it.

The generation ID connects the local result to the service's accounting. Find it in Activity, or use the generation metadata endpoint to inspect provider attribution, usage and cost. Record the returned model and actual provider along with the request settings. Asking for a model and learning who served it are separate steps.

If a call times out, the remote outcome can be unknown. Inspect Activity before manually repeating it. If an answer is truncated, look at its finish state and usage rather than sending the unchanged request repeatedly. The error guide helps distinguish failures, but your application decides which ones merit another attempt.

When a second model becomes useful

Once you have reviewed several representative inputs, a second acceptable model gives you a meaningful backup. OpenRouter accepts a models array in priority order. In the request object, replace the single model field with:

"models": ["YOUR_PRIMARY_MODEL_ID", "YOUR_APPROVED_FALLBACK_MODEL_ID"]

Fallback is triggered by request errors, including rate limits, availability problems and context-length failures. It does not read a completed handoff and decide that the model invented recovery. The response identifies the model ultimately used, and that model determines the request pricing. The provider restrictions must also leave the backup a usable route. See model fallback behavior.

Test the candidates separately before combining them. If the primary preserves the missing recovery evidence but the backup regularly fills it in, a successful fallback can make the application less useful while making its API-success chart look better. Keeping one model at first gives you a baseline from which that difference is visible.

OpenRouter earns its place when the freedom to try and operate different models saves more work than the extra routing layer creates. You can begin with a chat comparison, connect one acceptable model, and add alternatives when the results justify them. At that point the catalog has become something more useful than a long list of possibilities: a set of choices you understand well enough to use.

Sources & context

Sources linked in this article. Read alongside the author’s analysis; a citation does not independently verify a publisher’s claims.

Report an error or outdated detail

Related reading

Explore a related question