
# Pricing and currency conversion

Most providers we route to publish their per-token prices in USD
rather than EUR. To keep accounting and balances simple, **every
balance and every charge is in EUR**, and we convert non-EUR provider
prices to EUR once a day before they reach the catalogue.

This page explains how that conversion works, why the displayed price
on a USD-billed provider isn't quite the same as the headline USD
figure, and what happens when our FX feed is unavailable.

## How the conversion is computed

For each non-EUR provider price we ingest, the stored EUR value is:

```
stored_eur_per_1m_tokens =
    source_per_1m_tokens
    × max(source_to_eur_rate, 1.0)
    × (1 + fx_buffer_percent / 100)
```

- **`source_per_1m_tokens`** — the price the provider publishes in
  their billing currency (USD for most providers).
- **`source_to_eur_rate`** — the daily rate that converts the
  provider's currency into EUR, derived from the reference rates
  published by the European Central Bank at the
  [eurofxref-daily.xml](https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml)
  endpoint. A **rate floor of `1.0`** is applied: when the EUR is
  stronger than the source currency, the floor pins the conversion at
  parity so that a strong EUR can't quietly erode our markup.
- **`fx_buffer_percent`** — a fixed conversion markup applied on top of
  the ECB rate, defaulting to **3 %**. The markup covers FX-spread
  drift between the day we fetched the rate and the day we settle
  with the provider, plus payment-rail conversion fees.

For example, a provider publishing `$0.15 per 1M tokens` at an ECB
reference rate of `1 EUR = 0.90 USD` (so `1 USD = 1.111 EUR`) and a
3 % markup becomes:

```
0.15 × 1.111 × 1.03 ≈ 0.1717 EUR per 1M tokens
```

When the EUR is instead stronger than the dollar — say `1 EUR =
1.085 USD`, so `1 USD = 0.92 EUR`, which is below the `1.0` floor —
the floor kicks in and the conversion uses `1.0`:

```
0.15 × 1.0 × 1.03 ≈ 0.1545 EUR per 1M tokens
```

That is what your balance is debited for every 1M tokens you spend on
that model.

## Where you see the dual figures

Providers that already bill in EUR (e.g. `/providers/scaleway`)
render a single figure: their published EUR price, shown as-is with
no conversion. Providers that bill in USD (most of them — OpenAI,
Anthropic, Bedrock, Vertex, Groq, …) render both numbers:

```
$0.15/M (€0.17 billed)
```

The first figure is the upstream price you'd see on the provider's
own pricing page. The second is the EUR value your balance is
debited at, computed with the formula above. Hovering the price
shows the ECB rate, the markup, and when the conversion was last
refreshed.

## Refresh cadence

The ECB feed updates on TARGET business days at ~16:00 CET. We pull
it once per ingest run, which runs daily. Saturday and Sunday reuse
Friday's published rate — that's the standard ECB convention.

Your debit at request time uses the most recent stored EUR value.
**This means the price you see in the catalogue today may differ
slightly from what was billed yesterday for the same number of
tokens** — by the size of the FX move plus markup drift.

## When the ECB feed is unavailable

If we cannot reach ECB during an ingest run, we **soft-disable** the
affected providers (the USD-billed ones that need conversion) until
the feed recovers. While
soft-disabled, those providers are visible in the catalogue but won't
accept new requests, and an internal alert (`FX_INGEST_STALE`)
notifies the team.

We deliberately do not fall back to a hard-coded rate. A guessed rate
is worse than visible unavailability — it can either silently
under-charge our margin or over-charge customers, and neither is
something we want to do quietly.

## What we don't claim

- **We don't offer a rate lock.** The price you see today is the
  price we charge today; tomorrow's rate may differ. If you need a
  fixed price, the model's EUR figure is the one we honour at the
  moment of the request, not a quote held in advance.
- **We don't pass through every provider price change in real time.**
  The catalogue reflects the most recent successful ingest, which is
  daily. A provider price change mid-day will land in the next run.
- **We don't bill in any currency other than EUR.** EUR is our single
  operating currency: balances, charges, the price list, and checkout
  are all EUR. Providers that publish in USD are converted once a day
  as described above, which keeps a single ledger and a single set of
  accounting rules.

## Configuration

For self-hosted operators, the conversion markup can be tuned via the
`LOWROUTER_FX_BUFFER_PERCENT` environment variable (default `3.0`,
clamped to `[0, 20]`). The change takes effect on the next ingest
run.
