New features, improvements and fixes in EUrouter.
EUrouter now sends a request only to providers that have a known price for that kind of request: chat, embeddings,
rerank, transcription, speech or OCR. When a model has several providers and one has no price, your request goes to the
next one. When none of them has a price, you get a 400 saying the model is temporarily unavailable for this endpoint,
so you can pick another model.
Providers that charge in US dollars are converted to euros with the European Central Bank's latest reference rate. If
that rate can't be refreshed for 48 hours, those providers are paused instead of being billed at an estimated rate.
Free models (:free) keep working as before.
/api/v1/providers now returns each provider's owner (legal entity, ultimate parent company, its country and whether
it is EU-owned), its certifications with a link to the evidence, and its data_retention terms with their source.
Certifications past their expiry date are left out. The new /api/v1/providers/{slug} returns one provider.
/api/v1/models/{author}/{slug}/endpoints now returns, for every endpoint, the processing_countries where a request
may be processed and the endpoint's own data policy, certifications and retention terms.
We are verifying these facts provider by provider. Until a fact is verified, its field is null, never a guess.
When you are signed in and pick a paid plan on the pricing page, EUrouter now opens the plan upgrade page in your dashboard with that plan already selected. Before, you landed on the dashboard home and had to find Billing and Upgrade yourself. In an organization workspace, owners and admins land on the organization's plan page. Nothing is charged until you confirm the change.
Requests to cohere-embed-v4, cohere-embed-english-v3 and cohere-embed-multilingual-v3 on /embeddings now report
and bill the input tokens the provider counts. If the provider doesn't return a count, usage is estimated at about four
characters per token. Before, usage.prompt_tokens and the cost were based on four tokens per input character, so they
came out far higher than the real count.
/v1/embeddings now accepts an optional input_type: search_document, search_query, classification or
clustering. Cohere embed models use it, so you can embed search queries as search_query and stored texts as
search_document for retrieval. Before, every Cohere embedding was created as search_document, whatever you sent.
When you leave input_type out, Cohere still gets search_document, and other embedding models ignore the field. Any
other value returns a 400.
Chat models that accept images are now labeled Vision on their model page instead of Image Gen, and show their Tools and JSON Mode badges again. Transcription, rerank and decision models now show Transcription, Rerank or Decision instead of Chat, and embedding models are labeled Embedding even when their name doesn't say so.
GET /api/v1/models and a model's endpoints list now include stream in supported_parameters whenever a provider
streams that model, so you can filter on it with ?supported_parameters=stream. Before, most models left it out. The
model pages on the website now show the Streaming badge for those models, and transcription models no longer show a JSON
Mode badge, since their response_format picks the transcript format, not JSON output.
The Activity chart on the Activity page now draws requests against their own scale on the right, while tokens keep the scale on the left. Before, both shared the token scale, so the requests line sat flat at zero whenever your token volume was much larger than your request count. Both scales show compact numbers such as 4.5K and 1.2B.
Every page in the API reference now shows the full description of each endpoint above its parameters,
such as when a Generation lookup can briefly return 404. The Credits and
Providers pages also explain what their fields mean and how to use them, for example which
wallet the credit totals belong to and how provider slugs feed your routing preferences.
A model with the :free suffix now routes only to providers whose every price is zero. Before, :free checked only the
input and output token prices, so a rerank, transcription or OCR model that charges per request, per audio second or per
page could still be picked and billed. Such a :free request now returns an error saying no provider is available, so
you are never charged for it.
On /v1/responses, an input_image or input_audio part in an input item written as { "role": "user", "content": [...] },
without "type": "message", now routes your request only to providers that can read images or audio. Before, EUrouter
detected these parts only in items with "type": "message", so such a request could reach a provider that can't read
them.
The docs now have a Routing Rules page. It explains how to create a rule in the
dashboard, the scopes a rule can have, how to apply one with rule_id or rule_name or as an API key's default, how a
rule combines with the model and provider fields of your request, and how to test a rule with a dry run.
The "Learn more" link after you create a rule in the dashboard now opens this page.
A transcription request sent as a file upload (multipart/form-data to /audio/transcriptions) no longer fails with a
400 when a routing rule, or your API key's default routing rule, sets provider preferences. The provider form field,
sent as a JSON string, is now combined with the rule's preferences the same way as in a JSON request.
You can now pick the OCR model for PDFs yourself: plugins: [{ "id": "file-parser", "pdf": { "engine": "ocr", "model": "paddleocr-vl-1.6" } }].
Any catalog model that reads images or PDFs works, including the new OCR models paddleocr-vl-1.6 (Serenity Edge),
lightonocr-2-1b (IONOS) and lightonocr-3-0.8b (Scaleway), and zhipu/glm-ocr and deepseek-ocr-2.
The OCR call is routed like any other request, so your provider settings and your own provider keys apply, and its cost
at that endpoint's prices is included in usage.cost. Models that read PDFs get the whole file. Models that only read
images get one page per request, rendered inside our EU cluster, for up to 100 pages. The file annotation in the
response works as before, so sending it back skips OCR on the next request.
The code example for a model, in an expanded row on /models and on the model's own page, now calls the
endpoint that model supports. Embedding models show /embeddings, rerank models /rerank, transcription models
/audio/transcriptions and decision models /systemone, each in cURL, Python and JavaScript. Before, every model showed
a chat completions example, which failed for these models when copied.
The model page now has the same cURL, Python and JavaScript tabs as the models list.
PDFs sent to a model that can't read files are now read with Mistral OCR 3 on Mistral's EU endpoint, so scanned PDFs
work too. Choose it explicitly with plugins: [{ "id": "file-parser", "pdf": { "engine": "mistral-ocr" } }], or leave
the plugin out to get it by default. OCR costs $2.50 per 1,000 pages, and usage.cost includes it. With your own Mistral
key, Mistral bills the pages to you and we add your plan's BYOK fee.
The default follows your provider settings: when they rule Mistral out (for example with only, ignore or
data_collection), EUrouter falls back to its free in-cluster text extraction, and an explicit "mistral-ocr" gets a
400 error. The file annotation in the response works as before, so sending it back skips OCR on the next request.
When a provider closes a streaming response after it has started, the error at the end of the stream now has code
PROVIDER_CONNECTION_FAILED and error_type: "upstream_error", and says the provider is temporarily unavailable.
Before, it had code PROVIDER_ADAPTER_ERROR and error_type: "server_error", and said Internal Server Error. On
/v1/messages, the stream's error event now has type overloaded_error instead of api_error.
When the last provider EUrouter tries for a streaming request answers with a gateway timeout before sending anything,
the response is now HTTP 504 and says the provider did not respond in time, instead of 502. A non-streaming request
that runs out of providers still gets 503, or 429 when every provider it tried was rate limited.
You can retry a 504 as is, because no output was sent. After a stream fails with PROVIDER_CONNECTION_FAILED,
EUrouter does not retry for you: discard the partial output you received, then retry the request.
You can now send a PDF file content part to any model on /v1/chat/completions, not only to models that read PDFs
themselves. When a model can't read the file, EUrouter extracts its text inside the EU, in our own cluster, and sends
the model that text. Nothing goes to a third-party parser, and the extraction is free.
The response tells you what was read: message.annotations gets a file annotation with the file's SHA-256 hash,
its name and the extracted text per page, and streams send it in a delta.annotations chunk just before the finish
reason. Send that assistant message back in your next request and EUrouter reuses the text instead of reading the file
again.
Choose the behavior with the OpenRouter-compatible file-parser plugin:
plugins: [{ "id": "file-parser", "pdf": { "engine": "pdf-text" } }] always extracts the text, "native" only uses
models that read PDFs themselves, and "cloudflare-ai" is accepted as another name for "pdf-text". Text extraction
reads PDFs with a text layer, up to 500 pages per request. A scanned PDF without text gets a 400 error.
The Top Apps list on Rankings shows each app's site icon again. Icons load from eurouter.pro, so your browser doesn't contact the apps' websites or a third-party logo service. Apps whose icons are unavailable show their initials.
Nebius no longer commits to processing requests on its public endpoints inside the EU, so EUrouter has stopped routing to it. Models that Nebius served alongside other providers keep running on those EU providers. Hermes 4 405B was only available through Nebius and is retired; DeepSeek V4.1 Flash is its replacement. Nebius keys can no longer be added under BYOK, and you can delete a stored one on the BYOK page.
The file_data field of a file content part on /v1/chat/completions now also accepts a public https link to a
PDF, next to the base64 data URL it already took. EUrouter downloads the file inside the EU and passes the bytes to the
model, so the provider never sees your link.
Links must point to a public address, follow at most five redirects, and together stay under 25 MiB and download within
20 seconds per request. The file must be a PDF. A link that doesn't meet these rules gets a 400 error that doesn't
repeat the link.
provider.quantizations now works. List the precisions you accept, such as ["fp8", "bf16"], in a request or a
routing rule, and EUrouter only routes to endpoints served at one of them, fallbacks included.
fp4 also matches mxfp4 and nvfp4, and fp8 also matches mxfp8.unknown, and is only used when you list unknown.400 instead of being served at another precision.Each endpoint's precision is on its model page and in the models API. See Quantization.
EUrouter now only sends broadcast traces, including test traces, to https endpoints at public addresses. It no longer
follows redirects, so a destination that answers with a redirect shows as a failed send: use the final URL as the
endpoint.
A failed test shows the HTTP status your destination returned, without its response body. Testing a destination before you save it now needs the same admin role as creating one.
When you send an image_url to a model served by AWS Bedrock, EUrouter downloads the image inside the EU and passes
the bytes to the model. That download now only accepts https links to public addresses, follows at most five
redirects and stops reading once an image passes the 3.75 MB limit.
A link that doesn't meet these rules gets a 400 error. Base64 data URLs keep working as before.
Logos on the models, providers, rankings, BYOK and settings pages used to load from each provider's own website and from a logo service. That shared your IP address with those third parties. Every logo is now served from eurouter.pro itself, so browsing EUrouter sends no requests to them.
The dashboard home has a new Cache card, for personal accounts and organizations. It shows how much of your input was read from a provider's prompt cache, how that compares with the previous period, and an estimate of the money caching saved you at current prices.
A bar shows the cached share of your input tokens at a glance. If your providers don't cache, the bar stays empty.
The Requests table on the Activity page has two new columns:
Use them to compare models and providers on speed with your own traffic. Requests that weren't streamed have no TTFT, so both columns stay empty for them.
When you set provider.order with allow_fallbacks: false and a listed provider was briefly unavailable, a request
could be served by a provider outside your list. It now stays within the providers you listed: if none of them can
serve it, you get an error instead of a response from a provider you excluded.
Decision models answer questions you define about your content and return a probability for every option, so your code can act on confident answers and route borderline cases to a person.
clef-flash is now available on Berget and Regolo, next to metis on GreenPT. Read the
Decisions guide to get started, or see the Decisions page for what you
can build with them.
Requests that use OpenRouter's newer provider fields no longer fail with a generic 400.
zdr: true means no data retention. If you also set max_retention_days, the stricter of the two wins.sort in its object form, preferred_min_throughput and preferred_max_latency are accepted.null is accepted for every field and means "not set".max_price, are rejected with an error that names the field, so a hard limit is
never silently ignored.Retention settings only get stricter across your workspace defaults, routing rules and the request: a request can tighten retention, never loosen it. See Routing.
Signing up, logging in and resetting your password no longer show a captcha. Your accounts are protected by rate limits per IP address and per email address instead, so one inbox can't be flooded with reset or verification emails.
The captcha provider is also gone from our list of sub-processors.