Open WebUI API: sending requests to your models from code

Open WebUI has its own API: the models you see in the chat are reachable from scripts. We show how to get a key, which endpoints to call and where the two keys get mixed up.

5 min · · API keys · development

Open WebUI is usually seen as a web interface for chatting, but it has its own API. Through it a script, bot or another program can send a request to the same models connected in your Open WebUI, including models behind a gateway. Below we cover how to enable a key, which endpoints to call, and how the "open webui api" differs from calling the gateway directly.

The description relies on the Open WebUI documentation (the API Endpoints section and the API keys page). The docs state that parts of the interface are experimental and may change, so check menu names and endpoints in your own version.

Two keys that are easy to mix up

When Open WebUI is connected to a gateway, two different keys are involved:

  1. The gateway key fetch_.... It is entered in the Open WebUI connection (address https://api.zukko.pro/v1) and goes only to the gateway server. Setup is described in Open WebUI: connecting to Zukko.
  2. The Open WebUI user key. Open WebUI issues it itself, and your code sends it to the address of your Open WebUI.

Your script should never know the gateway key: it calls Open WebUI, and Open WebUI calls the gateway. That helps when the gateway key must stay on the server and users only need access to the interface.

Getting an Open WebUI key

According to the Open WebUI docs, a key takes a few steps.

  1. An administrator enables API keys globally: Settings, Admin, Authentication, the API Keys toggle. While it is off, no keys can be created or used, even for admins.
  2. A regular user needs the "API Keys" permission in group rights (Admin Panel, Users, Groups, Features Permissions). An admin can create a key right away.
  3. The user opens their profile, Settings, Account, the API keys section, clicks Show and creates a new secret key.

Things to know: each account has exactly one key. Creating a new one replaces the old one, and the old key stops working immediately. The key acts as the user and inherits their rights. It has no expiry, so rotation is manual. According to the docs, keys look like sk- followed by 32 hex characters.

So create a separate non-admin account for every integration, for example ci-bot, and generate the key from it.

Which endpoints to call

The docs examples use http://localhost:3000; yours will differ. Every request carries the header Authorization: Bearer your_key.

Model list

bash
curl -H "Authorization: Bearer YOUR_API_KEY" http://localhost:3000/api/models

GET /api/models returns the models available in your Open WebUI. Take the identifier for a request from there instead of guessing: you will see the name under which the model is published in your installation.

Requesting a model

The main endpoint is POST /api/chat/completions, OpenAI-compatible.

bash
curl -X POST http://localhost:3000/api/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-5-5",
    "messages": [{"role": "user", "content": "Explain prompt caching in two sentences"}]
  }'

The same in Python:

bash
import requests

BASE = "http://localhost:3000"
TOKEN = "YOUR_API_KEY"

resp = requests.post(
    f"{BASE}/api/chat/completions",
    headers={"Authorization": f"Bearer {TOKEN}"},
    json={
        "model": "claude-sonnet-5-5",
        "messages": [{"role": "user", "content": "Hello"}],
    },
    timeout=120,
)
resp.raise_for_status()
print(resp.json()["choices"][0]["message"]["content"])

Since the format is OpenAI-compatible, you can also use the official SDK with the address of your Open WebUI plus /api as base_url: the client appends /chat/completions itself. If that does not work for you, go back to plain requests as above.

bash
from openai import OpenAI

client = OpenAI(base_url="http://localhost:3000/api", api_key="YOUR_API_KEY")
r = client.chat.completions.create(
    model="claude-sonnet-5-5",
    messages=[{"role": "user", "content": "Hello"}],
)
print(r.choices[0].message.content)

The Anthropic format

Open WebUI also has an Anthropic-compatible endpoint, POST /api/v1/messages, which accepts an x-api-key header or Bearer. For the Anthropic SDK the address is given as .../api without /v1, otherwise the path doubles. According to the docs, if the Open WebUI connection speaks the OpenAI format (as in the setup with https://api.zukko.pro/v1), the request is converted to chat completions internally and reaches the gateway as an OpenAI request; for Claude this means all input is billed at the input price, with no cache discount. Connections that themselves speak the Anthropic Messages API receive the request without conversion, per the docs.

Tools and token usage

If tools (including MCP) are configured in Open WebUI, you can enable them from the API by passing identifiers in the tool_ids field. For MCP servers the identifier looks like server:mcp:<server id>. The docs warn separately: if the server is protected by OAuth, the key's user must have completed authorization in the web interface beforehand.

For counting tokens in a reply that used tools, the docs separate the fields. prompt_tokens and completion_tokens refer to the last model call, while input_tokens and output_tokens cover the whole reply, which may include several calls. If you compute spend from these fields, use the second pair. Checking final totals against the spending history in the gateway account is still more reliable.

Who pays and how to limit spend

Money is charged from the gateway balance through the fetch_... key entered in the connection, not through the Open WebUI key. So anyone holding an Open WebUI key spends your balance. Limit the risk on the gateway side: create a separate key for Open WebUI with a daily and monthly dollar limit and a list of allowed models. How that works is in the key limits docs and the article how to set a limit on an API key.

On the Open WebUI side you can restrict which routes keys may call: the Endpoint Restrictions setting takes a comma-separated list of paths (for example /api/models and /api/chat/completions). The restriction is shared by all keys of the installation.

Errors

  • 401: the Open WebUI key is wrong, was replaced by a newer one, or the account is inactive.
  • 403: the global key toggle is off, the user lacks the key permission, or the route is not in the allowed list.
  • Gateway errors pass through: according to the Open WebUI docs, when a provider rejects a request the endpoint returns the provider's status and error text. So 402 (not enough balance), 403 model_not_allowed_for_key or 429 for a key limit appear in the Open WebUI response. The full list of gateway codes is in the chat completions reference.

FAQ

Do I need to connect Open WebUI to a gateway to use its API?

No, the API works with any connected models. But to reach Claude, GPT or DeepSeek through one key, connect the gateway as an OpenAI-compatible provider.

Can I issue several keys to one user?

No. According to the docs, an account has one key and a new one replaces the old. For several integrations create several accounts.

How is this better than calling the gateway directly?

It is neither better nor worse, just a different setup. A direct request is simpler and gives you caching and accounting without an intermediary. Through Open WebUI you get user accounts, permissions, filters and shared model settings.

Where do I find the current list of endpoints?

In the Open WebUI docs and in the Swagger of your installation. According to the docs, Swagger is available at /docs if you start Open WebUI with the environment variable ENV=dev.

To try it, create a key in your account, connect it to Open WebUI, and obtain the user key using the steps above.

zukko

Pay 25×
less

for Claude Code & ChatGPT

One fetch_* key and base URL api.zukko.pro — for Cursor, Claude Code, Codex, and any SDK.

Get API keyDocumentation

api.zukko.pro · fetch_*

Open WebUI API: key and model requests from code · Zukko