Get API key
A thick impasto oil painting of an orchard at dusk under a heavy amber sky, rows of dark trees, one lantern hanging from a low branch and lighting the grass.
One lantern, and the rows it lights.
Blog

Keeping prompts in the EU

Many teams in Europe can use a hosted model only if the prompt stays in the EU. Often that is a contract with a customer, sometimes it is their own policy, and either way it has to hold on every request, including the ones that go wrong. On llm.buzz it is one field in the request, and it is not a paid tier.

Every provider says where it serves

Each provider on llm.buzz serves from the EU or from outside it, and every model page shows which, one row per provider. In the API, an EU provider’s tag ends in /eu, such as scaleway/eu. The endpoints route lists a model’s providers with their tags:

curl https://api.llm.buzz/api/v1/models/surogate/surogate3.7-flash/endpoints \
  -H "Authorization: Bearer $LLM_BUZZ_KEY"
One model's providers, each with its prices, limits and tag.

Keeping a request on EU providers

List the EU tags in provider.only:

reply = client.chat.completions.create(
    model="surogate/surogate3.7-flash",
    messages=[{"role": "user", "content": "Draft a reply to this complaint"}],
    extra_body={"provider": {"only": ["simpleminds-eu/eu", "surogate-spark/eu"]}},
)
Only the two EU providers of this model may serve the request.

Use the full tag, with /eu. A name matches a provider’s display name, its tag or the first part of its tag, so scaleway alone matches by the first part of its tag and would also match a Scaleway provider outside the EU, should one join, while scaleway/eu matches only the EU one.

Failover stays inside the list

only is a filter, not a first choice. The providers it leaves out are gone for this request, so failover moves within your list and never past it. If simpleminds-eu/eu times out, surogate-spark/eu answers. If both fail, you get the usual retryable 502 or 503, and the request is not sent anywhere else. It fails closed.

That matters more than the happy path. A residency rule that holds only while the first provider is healthy is not a rule. Retry with backoff, as for any 5xx, and the retry is held to the same list.

If none of the names you list serves the model, the answer is 404 no_allowed_providers, its message names the providers that do serve it, and nothing is sent to a provider.

One case to know about

A request that continues an earlier one, a Responses previous_response_id or a reasoning continuation, can stay on the provider that served the first, whatever only says. So send only from the first request of a conversation, not from the second: then the provider the conversation is tied to is already an EU one.

Where data goes is half the question

Residency says where a prompt is processed. The other half is what the provider does with it. Each provider declares whether it trains on prompts and how many days it keeps them, and the model page shows that declaration on every row, with who made it: the provider, or Surogate for a cloud provider it connects. Two request fields keep a request away from every provider whose declaration does not qualify:

FieldProviders that qualify
provider.zdr: trueDeclare that they keep prompts for 0 days.
provider.data_collection: "deny"Declare that they do not train on prompts and keep them for 0 days.

They combine with only:

extra_body={"provider": {
    "only": ["simpleminds-eu/eu", "surogate-spark/eu"],
    "data_collection": "deny",
}}
EU providers that neither train on prompts nor keep them.

A provider that has not declared a policy never qualifies. When no provider of the model qualifies, the answer is 404 model_not_available_under_data_policy, and again nothing is sent to a provider.

Setting it once for the whole organization

Repeating a field on every request works until someone forgets it. On the console’s Prompt logs page, an organization’s admin can set its data policy to no training and no retention. It applies from the next request, to every key, and a request can tighten it but never loosen it: a request without the field still gets the organization’s policy.

A request with either demand, or sent while the organization’s policy is on, is never kept in llm.buzz’s prompt logs. llm.buzz still holds it for up to 24 hours where a retry with the same Idempotency-Key, or a Responses continuation, needs it. We would rather say that plainly than round it down to zero.

Checking it yourself

None of this asks you to trust a badge. The model page lists every provider with its location and its declared data policy, the endpoints route gives your code the same facts, and a request that cannot be served under your rules is refused before it leaves llm.buzz. If a model has no EU provider today, you find out from a 404, not from an invoice.