Skip to main content
Percher is still being built and account creation is paused — get notified when it opens.

Bring your own LLM provider

Call any OpenAI-compatible API with your own key — no platform setup

Any OpenAI-compatible endpoint works from a Percher app with no platform configuration, as long as it answers inside the egress proxy's 30-second idle timeout — stream long completions, or a slow non-streaming call can be cut. Your app calls the provider with your own key, so the request never touches Percher's managed-inference service. It does still leave through Percher's egress proxy, like other server-side outbound traffic — use an HTTPS endpoint.

Use this when you already have a provider account, need a specific model, or want inference under a particular jurisdiction. Use managed inference instead when you want no provider account at all — there Percher holds the key and meters the cost.

Setup

bunx percher env set LLM_API_KEY --from-file ./key.txt
bunx percher env set LLM_BASE_URL=https://your-provider.example/v1
bunx percher publish

Then the standard SDK, pointed at your provider:

import OpenAI from "openai";

const ai = new OpenAI({
  apiKey: process.env.LLM_API_KEY,
  baseURL: process.env.LLM_BASE_URL,
});

const res = await ai.chat.completions.create({
  model: process.env.LLM_MODEL ?? "your-provider-model-id",
  messages: [{ role: "user", content: prompt }],
});

Use your provider's documented base URL verbatim — some publish it with a /v1 suffix and some without, and the SDK appends the route path to whatever you give it.

Outbound traffic already works

Apps reach the public internet through the forward proxy Percher injects — seven variables: HTTP_PROXY, HTTPS_PROXY, NO_PROXY=localhost,127.0.0.1,.local, each one's lowercase twin, and NODE_USE_ENV_PROXY=1. The proxy does not filter destinations, so any public provider hostname works without configuration. Bun and Node 22.21+/24+ honor it automatically; Python and Go honor the proxy variables natively; Node 20 and the 21/23 lines need setGlobalDispatcher(new ProxyAgent(process.env.HTTPS_PROXY)) from undici at startup.

Anonymous apps are the exception. An unclaimed app has no outbound access at all — it runs on an isolated network the proxy is not attached to. Claim it, or publish while signed in, before expecting a provider call to connect. On a claimed app an ENETUNREACH means the runtime is not using the proxy; on an anonymous app it is expected.

Keep the key server-side

LLM_API_KEY matches no public build prefix, so it is not forwarded to the build and will not reach the client bundle. That is the default you want. Two platform mechanisms can override it — renaming the key to VITE_*, NEXT_PUBLIC_*, or another public prefix, and listing it in [build] pass_env — so do neither. Your own code can still leak it: don't serialize it into a response, a client bundle, or a custom Dockerfile layer. Call the provider from a server route, API handler, or server action — a key shipped to the browser is readable by every visitor, and browser-side calls bypass the egress proxy entirely. LLM_BASE_URL and LLM_MODEL are not secrets and may be public if your framework needs them at build time.

Fail before the build, not after

Declare the keys so a missing value is caught at upload time instead of 90 seconds into a build:

[env]
required = ["LLM_API_KEY", "LLM_BASE_URL"]
optional = ["LLM_MODEL"]

A missing required key returns REQUIRED_ENV_MISSING with the exact key list.

What this covers

Anything speaking the OpenAI chat-completions format: hosted providers, EU- or Sweden-based endpoints such as staik, aggregator gateways, and self-hosted servers like Ollama or vLLM reachable over the public internet. Percher does not operate, vet, or have a commercial relationship with these providers — the list is illustrative of the API shape, not a recommendation. An endpoint on your own private network is not reachable: your app runs on Percher's infrastructure, not yours, so the provider needs a public hostname.

Data protection

You remain the controller for prompt content, and you contract directly with the inference provider in addition to your agreement with Percher — Percher still hosts the process and handles the key. These calls do not go through Percher's managed-inference path, so its approval gate does not apply.

Two things to be precise about. Outbound traffic traverses Percher's egress proxy — with an HTTPS endpoint the proxy carries an opaque tunnel and cannot read request or response bodies, which is the reason to insist on HTTPS. And your key is encrypted at rest, but Percher's control plane necessarily handles it in plaintext when you set it and again when it is injected at deploy time: it is a secret shared with the platform, exactly like any other env var. Check where your provider processes data and what it retains before sending personal data.