Integrations
Anything that speaks the OpenAI chat API can speak to OpenFill. Three values do it: the base URL, your key, and a model id. The fourth is where the max price goes, and that is what each section below settles.
Before any of them
- Set a max price on the key or on your account, on Bidding. A tool that cannot add a field or a header still works once that default exists, and a tool that can add one overrides it per request.
- Use
https://api.openfill.ai/v1as the base URL, with the/v1. Every tool below expects that shape. - Verify from a shell before configuring the tool. The first call lists the models; the second is a completion that returns the receipt.
Set OPENFILL_API_KEY to a key from API keys before running an example. A key starts with of_live_ and is shown once.
curl -s https://api.openfill.ai/v1/models -H "Authorization: Bearer $OPENFILL_API_KEY" curl -s https://api.openfill.ai/v1/chat/completions \ -H "Authorization: Bearer $OPENFILL_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model": "placeholder-large", "messages": [{"role": "user", "content": "hi"}], "max_price": 0.10}'| Where the max price can go | Which tools |
|---|---|
max_price in the body | The OpenAI SDKs (Python via extra_body, TypeScript via a cast), curl, any raw HTTP client. |
The X-Max-Price header | The TypeScript SDK, the Vercel AI SDK, LangChain, LiteLLM, the Agents SDK, OpenCode, Continue, Kilo Code. |
| The key or account default | Cline, Roo Code, Cursor, Aider, Open WebUI, and anything else with a settings screen and no header field. |
The OpenAI SDKs
Point the client at the base URL and pass the max price by the route the SDK offers. Python takes unknown fields through extra_body. The TypeScript types refuse a field they do not declare, so the header is the plain route, and // @ts-expect-error above max_price is the body route.
import osfrom openai import OpenAI client = OpenAI( base_url="https://api.openfill.ai/v1", api_key=os.environ["OPENFILL_API_KEY"],) r = client.chat.completions.create( model="placeholder-large", messages=[ {"role": "user", "content": "hi"} ], extra_body={"max_price": 0.10},)print(r.choices[0].message.content)print(r.usage)import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.openfill.ai/v1", apiKey: process.env.OPENFILL_API_KEY,}); const r = await client.chat.completions.create( { model: "placeholder-large", messages: [{ role: "user", content: "hi" }], }, { headers: { "X-Max-Price": "0.10" } },);console.log(r.choices[0].message.content);console.log(r.usage);curl https://api.openfill.ai/v1/chat/completions \ -H "Authorization: Bearer $OPENFILL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "placeholder-large", "messages": [ {"role": "user", "content": "hi"} ], "max_price": 0.10 }'Go, Java and Ruby have their own routes for an undeclared body field. They are on the compatibility page.
Any tool that reads the OpenAI variables
Both official SDKs, and most tools built on them, read OPENAI_BASE_URL and OPENAI_API_KEY from the environment. Set those two and code that names no base URL reaches OpenFill unchanged. The max price then comes from the key or the account.
# A key from API keys, shown once when madeexport OPENFILL_API_KEY="of_live_..." # Any tool that reads the OpenAI variablesexport OPENAI_BASE_URL="https://api.openfill.ai/v1"export OPENAI_API_KEY="$OPENFILL_API_KEY" # The max price comes from the key or the# account default, set on Bidding.curl -s "$OPENAI_BASE_URL/models" \ -H "Authorization: Bearer $OPENAI_API_KEY"Aider, CrewAI and Pydantic AI are among the tools that work this way. Aider also takes the same pair as OPENAI_API_BASE, and prefixes the model with openai/:
export OPENAI_API_BASE="https://api.openfill.ai/v1"export OPENAI_API_KEY="$OPENFILL_API_KEY"aider --model openai/placeholder-largeVercel AI SDK
Use the OpenAI-compatible provider package rather than the OpenAI one, which now defaults to the Responses API. headers carries the max price on every call; ask for includeUsage on a stream to see the receipt in the last chunk.
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";import { generateText } from "ai"; const openfill = createOpenAICompatible({ name: "openfill", baseURL: "https://api.openfill.ai/v1", apiKey: process.env.OPENFILL_API_KEY, headers: { "X-Max-Price": "0.10" },}); const { text } = await generateText({ model: openfill.chatModel("placeholder-large"), prompt: "hi",});console.log(text);LangChain
ChatOpenAI with a base URL. LangChain's OpenAI class keeps to the documented OpenAI response fields, so usage.x_market and reasoning_content are dropped on the way through. Read them from the dashboard, or through the raw SDK when you need them in code.
import osfrom langchain_openai import ChatOpenAI llm = ChatOpenAI( model="placeholder-large", base_url="https://api.openfill.ai/v1", api_key=os.environ["OPENFILL_API_KEY"], default_headers={"X-Max-Price": "0.10"},)print(llm.invoke("hi").content)LlamaIndex uses the same three values through OpenAILike, with is_chat_model=True and is_function_calling_model=True.
LiteLLM
Prefix the model with openai/ and give api_base. The same three values in a proxy config.yaml put OpenFill behind LiteLLM's own endpoint, which is also the route for a tool that speaks a different API.
import osfrom litellm import completion r = completion( model="openai/placeholder-large", api_base="https://api.openfill.ai/v1", api_key=os.environ["OPENFILL_API_KEY"], messages=[ {"role": "user", "content": "hi"} ], extra_headers={"X-Max-Price": "0.10"},)print(r.choices[0].message.content)# config.yaml, for litellm --config config.yamlmodel_list: - model_name: placeholder-large litellm_params: model: openai/placeholder-large api_base: https://api.openfill.ai/v1 api_key: os.environ/OPENFILL_API_KEY extra_headers: X-Max-Price: "0.10"OpenAI Agents SDK
Give the SDK a client pointed at OpenFill and select the chat completions API, since the SDK's default is the Responses API. Tracing posts to OpenAI with whatever key the client holds, so switch it off.
import osfrom openai import AsyncOpenAIfrom agents import ( Agent, Runner, set_default_openai_api, set_default_openai_client, set_tracing_disabled,) set_default_openai_client(AsyncOpenAI( base_url="https://api.openfill.ai/v1", api_key=os.environ["OPENFILL_API_KEY"], default_headers={"X-Max-Price": "0.10"},))set_default_openai_api("chat_completions")set_tracing_disabled(True) agent = Agent(name="assistant", model="placeholder-large")print(Runner.run_sync(agent, "hi").final_output)OpenCode
A custom provider in opencode.json, built on the AI SDK's OpenAI-compatible package. The key is read from the environment and the max price rides in options.headers.
// opencode.json{ "$schema": "https://opencode.ai/config.json", "provider": { "openfill": { "npm": "@ai-sdk/openai-compatible", "name": "OpenFill", "options": { "baseURL": "https://api.openfill.ai/v1", "apiKey": "{env:OPENFILL_API_KEY}", "headers": { "X-Max-Price": "0.10" } }, "models": { "placeholder-large": { "name": "placeholder-large" } } } }}Cline
Cline, Roo Code and Kilo Code share one settings screen for this: choose the OpenAI Compatible provider and fill in the base URL, the key and the model id. Cline and Roo Code have no header field, so set the max price on the key. Kilo Code has a headers field, which can carry X-Max-Price per tool.
# Cline, Roo Code and Kilo Code# Settings > API Provider: OpenAI Compatible Base URL https://api.openfill.ai/v1API Key (your OPENFILL_API_KEY)Model ID placeholder-large # These tools send no max price of their# own. Set one on the key, on Bidding.Roo Code uses native tool calling only, which OpenFill's chat endpoint supports. The input and output price fields these tools show are for a rate card; leave them empty, or fill them from the level of the moment, and read the real charge from your requests.
Cursor
- Open Cursor's settings and go to Models.
- Paste your OpenFill key into the OpenAI API key field.
- Turn on Override OpenAI Base URL and enter
https://api.openfill.ai/v1. - Add a custom model named
placeholder-largeand select it.
Cursor sends every request through its own servers, so your key and your prompts pass through them. Only its chat features use a custom key. Cursor has no header field, so the max price comes from the key.
Continue
A model block in config.yaml with the openai provider and an apiBase. The max price rides in requestOptions.headers.
models: - name: OpenFill provider: openai model: placeholder-large apiBase: https://api.openfill.ai/v1 apiKey: ${{ secrets.OPENFILL_API_KEY }} requestOptions: headers: X-Max-Price: "0.10"Open WebUI
Under the admin settings, add a connection of the OpenAI type with the base URL and your key. Open WebUI checks the connection by listing the models, which needs no key here, and then offers every served model in its picker. The max price comes from the key.
Gateways and routers
A gateway that proxies OpenAI-shaped providers takes OpenFill with the same three values, and carries the max price as a header if it forwards custom headers, or from the key default if it does not. The public market data is worth wiring into a router's cost logic: GET /v1/market/levels answers with every market's level, without a key.
Tools that need another API
Claude Code speaks the Anthropic Messages API and Codex CLI speaks the Responses API. OpenFill serves the OpenAI chat and completions shapes, so neither tool can be pointed at it directly today. A translating proxy is the route in the meantime, and a tool that offers an OpenAI-compatible provider is the direct one.
A native listing
OpenFill is served through the OpenAI Compatible slot in the tools above. A tool that lists OpenFill by name changes nothing about the request; it saves typing the base URL.