What is an AI gateway, and what makes one European? OpenRouter vs Opper
An AI gateway is a single API between your app and many LLM providers. Without one, you end up rebuilding the same plumbing: a key per provider, an SDK per provider, retries, fallbacks, and a bill from everyone. A gatewa
An AI gateway is a single API between your app and many LLM providers. Without one, you end up rebuilding the same plumbing: a key per provider, an SDK per provider, retries, fallbacks, and a bill from everyone. A gateway puts all of that behind one endpoint.
A European AI gateway is one that runs in Europe, specifically the EU, and can keep your requests there, from the gateway itself to the model that answers.
This is a short, high-level comparison. I use OpenRouter and Opper as the two examples because they keep requests in the EU in different ways, and you can try both with the same OpenAI SDK.
What an AI gateway does
- One endpoint, many models. You send an OpenAI-style request and pick the model by name, and the gateway forwards it to a provider that serves it.
- Failover. The same open-weight model is often served by several providers, so if one is down or rate-limited the gateway retries on another.
- One key and one bill. Your app holds one gateway key instead of ten provider keys, and spend shows up in one place.
- Routing preferences. Cheapest, fastest or highest-throughput provider first, depending on what you ask for.
That covers most "should I use a gateway" decisions. Where it gets interesting is the data.
What makes a gateway European
"EU-hosted" gets used loosely. In practice there are four separate questions:
- Where does the gateway run, and under which company? Every prompt passes through it.
- Where does inference run? The same model can be served from the EU by one provider and from the US by another.
- What does the provider keep? Logging, retention and training on prompts differ per provider, not per model.
- What happens when the allowed route is down? The safe behaviour is to refuse the call. The common failure is a silent fallback to a provider in another region, which is exactly what failover is built to do.
Point 4 is the one people miss. Failover and data residency pull in opposite directions, so a European setup needs a way to say "fail over, but only inside this boundary".
OpenRouter and Opper side by side
| OpenRouter | Opper | |
|---|---|---|
| Company | Stripe (US), which acquired OpenRouter in 2026 | Opper Technology AB, Stockholm, Sweden |
| OpenAI-compatible base URL | https://openrouter.ai/api/v1 |
https://api.opper.ai/v3/compat |
| Keeping a request in the EU | Call the EU endpoint https://eu.openrouter.ai/api/v1, Business and Enterprise plans only |
Call an EU-hosted route on any account, or set a model access rule |
| Per-request data controls |
provider.zdr, provider.data_collection, provider.only / ignore
|
The route you call, provider/model
|
| Org-wide enforcement | Privacy settings on the account | Model access rule on the org or a project |
| No eligible route left | The request errors instead of leaving the region | Refused with 403 before it reaches a provider |
| Fee on credit purchases | 5.5%, or 8% on Business, which the EU endpoint needs | 3%, EU-hosted routes included (5.5% with Control Plane rules) |
On OpenRouter you don't pick a European model for this. You call the EU endpoint with the usual model id, and it only uses provider endpoints in the EU/EEA, the UK or Switzerland, so fewer models are available than on the global endpoint.
Both fail closed, which is the right call. The difference is where the boundary lives. OpenRouter draws it at the endpoint you call. Opper draws it in a rule on your org or project, so every key and every app under it inherits the same boundary. One more detail from OpenRouter's own docs: with the EU endpoint, prompts and completions stay in the region, while account, billing and other operational records may be processed in the US.
The same request on both
Both speak the OpenAI API, so switching is a base URL and a model name.
OpenRouter, EU endpoint, zero data retention providers only:
import os
from openai import OpenAI
client = OpenAI(
base_url="https://eu.openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
)
resp = client.chat.completions.create(
model="mistralai/mistral-small-2603",
messages=[{"role": "user", "content": "Summarise GDPR Article 28 in two sentences."}],
extra_body={"provider": {"zdr": True, "data_collection": "deny"}},
)
print(resp.choices[0].message.content)
Opper, an EU-hosted route:
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.opper.ai/v3/compat",
api_key=os.environ["OPPER_API_KEY"],
)
resp = client.chat.completions.create(
model="mistral/mistral-small-2603", # Mistral Small on Mistral's own EU route
messages=[{"role": "user", "content": "Summarise GDPR Article 28 in two sentences."}],
)
print(resp.choices[0].message.content)
On Opper the model id carries the route as provider/model, and each route's data policy is in the public catalogue. https://api.opper.ai/v3/models?q=mistral-small-2603 lists every route for that model, and for the one above it returns "inference_location": "EU", "training": "no" and "logging": "none".
To make it a rule rather than a habit, a model access rule allowlists EU inference locations for the whole org or a project. A call that names a US route then comes back as:
403 model "anthropic/claude-sonnet-5" is blocked by the active model allowlist: inference location US is outside EU
A bare model name, like mistral-small-2603 without a provider, still fails over, but only across the allowed routes, and it's refused with 403 if none are left. That's point 4 handled in one place.
How to pick
- You want the biggest community and the most integrations: OpenRouter is hard to beat.
- You need EU processing on OpenRouter: that means the Business plan, with an 8% fee on every credit purchase against Opper's 3%, and their docs say operational data may still be processed in the US.
- You want the gateway company, the gateway and inference in Europe, with one rule your whole org inherits: that's the case Opper is built for.
- Whichever you pick: test point 4 yourself. Block a region and see whether the call fails or quietly goes somewhere else.
Docs if you want to dig in: OpenRouter EU in-region routing, Opper model access rules.
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.