Skip to content

One line of code, and you run in the European Union.

The API is OpenAI-compatible. Existing SDKs, existing frameworks and existing code keep working: you swap the base_url and pick a tier instead of a model name.

Quickstart: your first API call

Create a key in your dashboard under Settings, API keys. You see the secret exactly once. After that we keep only a hash and the prefix. Put it in an environment variable, never in your repository.

curl
curl https://api.europeanailab.eu/v1/chat/completions \
  -H "Authorization: Bearer $UNION_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "eu-standard",
    "messages": [{"role": "user", "content": "Explain VAT reverse charge."}]
  }'
python
import os

from openai import OpenAI

client = OpenAI(
    api_key=os.environ["UNION_API_KEY"],
    base_url="https://api.europeanailab.eu/v1",   # the only line that changes
)

resp = client.chat.completions.create(
    model="eu-standard",
    messages=[{"role": "user", "content": "Explain VAT reverse charge."}],
)
print(resp.choices[0].message.content)

Node, Go, Java, .NET?

Every official OpenAI SDK works: set the base URL to https://api.europeanailab.eu/v1 and use your Union key. We implement the chat completions contract, including streaming, tools and JSON schemas.

Model tiers and context windows

You pass a tier in the model field, not a model name. That keeps your integration working when we replace the model underneath, and we will, because open-weight models get retired and overtaken.

TierContextInput / 1MOutput / 1MBest for
eu-lite131K€0.40€1.60Fast, cheap work: classification, extraction, short answers.
eu-standard262K€0.70€3.50The workhorse tier. Default for chat and most API traffic.
eu-long1,000K€1.00€2.00Whole case files, contracts and codebases in a single prompt.
eu-pro1,000K€4.00€12.00Heavy reasoning, agents, complex code.

Exceed a tier's context window and you get a 400 with the maximum in it: we never silently truncate your prompt.

Streaming responses

Streaming works the way you expect, with one important difference you need to know about because it concerns your money.

Turn on include_usage

With OpenAI-compatible streaming the API sends no token usage by default. Without stream_options: {"include_usage": true} you cannot see at the end of a stream what the call cost. We meter it either way: this is about you being able to see and check it too.
python
stream = client.chat.completions.create(
    model="eu-standard",
    messages=[{"role": "user", "content": "Write a summary."}],
    stream=True,
    # Without this there is no usage object and you cannot see the cost.
    stream_options={"include_usage": True},
)

for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="")
    if chunk.usage:                      # final chunk
        print("\n", chunk.usage)

Cut a stream off halfway and you only pay for what was generated up to that point. If the broken connection means no usage chunk arrives, we estimate the consumption and correct it in the daily reconciliation with our provider. Such a line sits in your usage overview as estimated until it is settled.

Function calling and tool use

Tools follow the familiar schema. If a model produces a tool call that does not match your schema, we log it: in version 1 we do not silently correct it, because quietly repairing your output is worse than a visible failure.

python
tools = [{
    "type": "function",
    "function": {
        "name": "find_invoice",
        "description": "Look up an invoice by number.",
        "parameters": {
            "type": "object",
            "properties": {"number": {"type": "string"}},
            "required": ["number"],
        },
    },
}]

resp = client.chat.completions.create(
    model="eu-standard", messages=messages, tools=tools
)

Structured output with a JSON schema

Pass a JSON schema and validate the answer on your side. Our advice: always validate, even when the model usually behaves.

python
resp = client.chat.completions.create(
    model="eu-standard",
    messages=[{"role": "user", "content": "Extract the name and amount."}],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "invoice",
            "schema": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "amount_eur": {"type": "number"},
                },
                "required": ["name", "amount_eur"],
            },
        },
    },
)

Response headers: cost, tier and routing

Every response tells you what happened. This is not a debugging extra, it is the point: you can check for yourself that you got what you paid for.

HeaderMeaning
X-EU-Model-TierThe tier this request ran on.
X-EU-RoutedPresent when the request was rerouted to another model, with the reason: censorship or error.
X-EU-Degradedtrue when the replacement model is weaker or has a smaller context window. You then pay the lower of the two rates.
X-EU-Refusal-ClassPresent on a refusal we could not reroute, for example A-unrouteable when no alternative has enough context.
X-EU-Tokens-In / -OutMetered tokens for this request.
X-EU-Cost-EURWhat this request took off your balance, in euros.
X-EU-Balance-EURYour remaining balance after this request.

Rate limits and spend ceilings

There is a limit per key and per account, plus the ceilings you set yourself. The latter matter more than they sound: without a ceiling, a leaked key or an infinite loop in your own code can drain a balance overnight.

  • Requests per minute, per key and per account.
  • Daily and monthly ceiling in euros, configurable per key.
  • New accounts: a maximum of €25 per day for the first 48 hours, whatever the balance.
  • If we see more than ten times your weekly average within an hour, we pause the key automatically and email you. Better one false alarm than an empty wallet.
  • A request only starts when your balance is at least €0.05.

Error codes and what to do

CodeMeaningWhat to do
400Invalid request, or a prompt above the context window.Fix the request; the maximum is in the error message.
401Unknown, revoked or paused key.Check the key in your dashboard.
402Insufficient balance.Top up. The response contains a direct link.
403Request falls outside the usage policy.See fair use below.
429Rate limit or ceiling reached.Wait and retry; respect Retry-After.
502 / 503Upstream model unavailable and no fallback possible.Retry. See the status page.

Failed requests cost you nothing: if no usable answer came back, nothing comes off your balance.

Fair use and usage policy

We refuse instructions for weapons, malware and fraud, and material that harms children. Those refusals are final: we never forward such a request to another model to get an answer out of it anyway.

A model refusing a legitimate question about politics, history or a socially sensitive subject is a different matter: we treat that as a defect and send it to a model without that blind spot. You see it in the header X-EU-Routed: censorship.

One of our models is licensed with its own usage policy. That policy applies in full: rerouting does not get around licence terms.

Not sure whether your use case fits? Email hello@europeanailab.eu. An answer up front is cheaper for both of us than a blocked key afterwards.

Frequently asked questions

The questions that arrive by email before anyone opens a key: what the API is compatible with, what a request costs, and where it runs.

Which OpenAI SDKs work with an OpenAI-compatible API?

All the official ones, because they are thin clients over the chat completions contract and take the base URL as a constructor argument. The Python and Node SDKs are the common cases; Go, Java and .NET work the same way. You keep your retry logic, your streaming handling and your types.

Can I use LangChain or the Vercel AI SDK with it?

Yes. Both expose the base URL as configuration on their OpenAI provider, so you point that at our host and use a Union AI key. Anything else that lets you override the base URL works on the same principle, including most agent frameworks and evaluation harnesses.

Does the API support streaming and function calling?

Yes, both, plus structured output against a JSON schema. Streaming responses carry token usage at the end of the stream, so you can meter a streamed request as precisely as a normal one.

What are the rate limits?

There is a limit on requests per minute per key and per account, plus daily and monthly ceilings in euros that you set yourself. New accounts are capped at €25 a day for the first 48 hours, whatever the balance. Hitting a limit returns a 429 with a Retry-After header.

How should I handle 429s and retries?

Respect Retry-After rather than retrying immediately, and back off exponentially with jitter so a burst does not synchronise across your workers. Do not retry a 402: that means the balance is empty and a loop will simply repeat the failure. One retry is usually enough, because a second failure is rarely a different answer.

How do I see what a request cost?

Every response carries headers: X-EU-Tokens-In and X-EU-Tokens-Out for the metered tokens, X-EU-Cost-EUR for what came off your balance, and X-EU-Balance-EUR for what is left. Your own logs can therefore answer the cost question per feature or per customer without opening a dashboard.

Which model runs behind each tier?

Each tier maps to a named open-weight model, published with its licence and context window on the AI transparency page. You buy the tier rather than the model name, so we can replace the model underneath without breaking your integration, and we tell you when we do.

Moving an existing integration across is three lines: the key, the base URL and a tier name. The whole change, plus what does not port, is in switching in three lines. Still open? The rates per tier are on the pricing page, the model and licence behind each tier on AI transparency, and what happens during an outage on the status page. Two longer reads: what an AI API costs per token and where your prompts actually go.

Start with AI for Europeans.

Use AI without sending your data abroad, everything on European servers, at a European price.

€13 a month for the chat, cancel any time. Or per token from €10 of credit.

Not sure yet? Take the 60-second quiz →