# Integrations

> The Markdown form of https://openfill.ai/docs/integrations. The index of every docs page is https://openfill.ai/llms.txt, and every page in one file is https://openfill.ai/llms-full.txt.

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 price limit goes, and that is what each section below settles.

## Before any of them

1. Set a price limit on the key or on your account, on [Bidding](https://openfill.ai/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. A fixed-price model needs none, and ignores one sent to it.
2. Use `https://api.openfill.ai/v1` as the base URL, with the `/v1`. Most tools below expect that shape; the rows that want the host alone or the full endpoint URL say so.
3. 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](https://openfill.ai/keys) before running an example. A key starts with `of_live_` and is shown once.

```bash
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": "market-preview", "messages": [{"role": "user", "content": "hi"}], "price_limit": 0.10}'
```

| Where the price limit can go | Which tools |
| --- | --- |
| `price_limit` in the body | The OpenAI SDKs (Python via extra_body, TypeScript via a cast), curl, Claude Code through CLAUDE_CODE_EXTRA_BODY, any raw HTTP client. |
| The `X-Price-Limit` header | The TypeScript SDK, the Vercel AI SDK, LangChain, LiteLLM, the Agents SDK, OpenCode, Continue, Kilo Code, Codex CLI. |
| The key or account default | Cline, Roo Code, Cursor, Aider, Open WebUI, and anything else with a settings screen and no header field. |

### Timeouts in the client

A request that waits for the level holds the connection open while it waits, so the client's timeout has to cover `max_wait` plus generation. The OpenAI SDKs default to ten minutes. On Node the built-in `fetch` stops waiting for response headers after five minutes, and a blocking request that waits longer fails in the client. A queued request is cancelled when its disconnect reaches the gateway; a request already generating can finish and be charged. For a longer wait, stream, where the first comment arrives at once, or send it detached. The edge in front of the API closes a blocking response that has carried no bytes for about two minutes, with a `524`, so the same two routes apply from that point whatever the client.

## The OpenAI SDKs

Point the client at the base URL and pass the price limit 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 `price_limit` is the body route.

```python
import os
from 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="market-preview",
    messages=[
        {"role": "user", "content": "hi"}
    ],
    extra_body={"price_limit": 0.10},
)
print(r.choices[0].message.content)
print(r.usage)
```

```typescript
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: "market-preview",
    messages: [{ role: "user", content: "hi" }],
  },
  { headers: { "X-Price-Limit": "0.10" } },
);
console.log(r.choices[0].message.content);
console.log(r.usage);
```

**curl**

```bash
curl "https://api.openfill.ai/v1/chat/completions" \
  -H "Authorization: Bearer $OPENFILL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "market-preview",
    "messages": [
      {"role": "user", "content": "hi"}
    ],
    "price_limit": 0.10
  }'
```

Go, Java and Ruby have their own routes for an undeclared body field. They are on the [compatibility page](https://openfill.ai/docs/compatibility#sdks).

## 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 price limit then comes from the key or the account.

```bash
# A key from API keys, shown once when made
export OPENFILL_API_KEY="of_live_..."

# Any tool that reads the OpenAI variables
export OPENAI_BASE_URL="https://api.openfill.ai/v1"
export OPENAI_API_KEY="$OPENFILL_API_KEY"

# The price limit 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/`:

```bash
export OPENAI_API_BASE="https://api.openfill.ai/v1"
export OPENAI_API_KEY="$OPENFILL_API_KEY"
aider --model openai/market-preview
```

## Claude Code

Claude Code speaks the Anthropic Messages API and OpenFill serves it natively at `POST /v1/messages`, with `count_tokens` beside it. The base URL goes without `/v1`, since the tool appends the path itself; the key travels as a bearer token; the model slots are set by hand because the tool's own discovery keeps only ids that name Claude.

```bash
# The base URL without /v1; Claude Code appends /v1/messages
export ANTHROPIC_BASE_URL="https://api.openfill.ai"
export ANTHROPIC_AUTH_TOKEN="$OPENFILL_API_KEY"
export ANTHROPIC_API_KEY=""

# Every model slot by hand: discovery keeps only ids that name Claude
export ANTHROPIC_MODEL="market-preview"
export ANTHROPIC_DEFAULT_SONNET_MODEL="market-preview"
export ANTHROPIC_DEFAULT_HAIKU_MODEL="market-preview"
export CLAUDE_CODE_SUBAGENT_MODEL="market-preview"

# The price limit and the wait, merged into every request body
export CLAUDE_CODE_EXTRA_BODY='{"price_limit": 0.10, "max_wait": 600}'
export CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1
claude
```

| Fact | What it means here |
| --- | --- |
| The vendor of Claude Code states that it is built for Claude models. | A different model through any gateway is outside what that vendor supports. |
| The tool's cost display is computed locally from token counts at a list price. | It means nothing for OpenFill. The charge is on the requests page and GET /v1/usage. |
| Two watchdogs cut a silent stream after about five minutes. | The gateway sends a ping event every ten seconds while an order waits, which both count. The whole-request timeout, ten minutes by default, is the one to raise for a longer wait, with the stream idle timeout beside it. |
| Context counting goes through count_tokens. | Served as the gateway's own prompt estimate, free of charge. Without it the gauge would be fed through a paid request. |
| An attribution block is prepended to the system prompt. | It reaches the model here. CLAUDE_CODE_ATTRIBUTION_HEADER=0 removes it. |
| Experimental betas add fields the endpoint refuses. | CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1 keeps tool references out of the request. |

## Codex CLI

Codex speaks the Responses API and OpenFill serves it natively at `POST /v1/responses`, so the tool is a provider entry in its config file. The price limit rides in a static header, and the model's context length is typed in because Codex assumes a default for an id it does not recognise.

```toml
# ~/.codex/config.toml
model_provider = "openfill"
model = "market-preview"
model_context_window = 262144
web_search = "disabled"
show_raw_agent_reasoning = true

[model_providers.openfill]
name = "OpenFill"
base_url = "https://api.openfill.ai/v1"
env_key = "OPENFILL_API_KEY"
wire_api = "responses"
http_headers = { "X-Price-Limit" = "0.10" }
```

| Fact | What it means here |
| --- | --- |
| Codex sends a web_search entry and a namespace entry in tools on every request. | The gateway drops every tool type other than function, so the request runs; the model sees the function tools alone. Disabling web_search in the config keeps the request honest about what the model can do. |
| Codex resets its idle timer only on a parsed event. | The gateway sends a response.in_progress event with every keepalive, so the default five-minute timer holds through a queue. |
| Reasoning arrives as raw reasoning_text. | Codex shows it only with show_raw_agent_reasoning on. |
| Codex sends store: false on every request. | The flag is accepted and dropped; storage follows the account setting, and retention_days is the per-request switch. |
| usage.x_market reaches Codex as metadata it does not display. | Spend is on the requests page and GET /v1/usage. |
| Codex sends no output cap. | A turn runs until the model stops or the model's max output is reached, so a request costs what the model chooses to say; the placeholder models, which never stop on their own, run to the cap. |

## 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 price limit rides in `options.headers`.

```json
// 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-Price-Limit": "0.10" }
      },
      "models": {
        "market-preview": { "name": "market-preview" }
      }
    }
  }
}
```

## 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 price limit on the key. Kilo Code has a headers field, which can carry `X-Price-Limit` per tool.

```text
# Cline, Roo Code and Kilo Code
# Settings > API Provider: OpenAI Compatible

Base URL   https://api.openfill.ai/v1
API Key    (your OPENFILL_API_KEY)
Model ID   market-preview

# These tools send requests without a price
# limit. 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. For a fixed-price model, fill them from its prices under `pricing` on `GET /v1/models`: the cache-miss price as input and the output price as output.

## Cursor

1. Open Cursor's settings and go to Models.
2. Paste your OpenFill key into the OpenAI API key field.
3. Turn on Override OpenAI Base URL and enter `https://api.openfill.ai/v1`.
4. Add a custom model named `market-preview` and 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 price limit comes from the key.

## Vercel AI SDK

Use the OpenAI-compatible provider package rather than the OpenAI one, which now defaults to the Responses API. `headers` carries the price limit on every call; ask for `includeUsage` on a stream so the last chunk carries usage. The receipt is in that chunk on the wire, and where the SDK's usage object does not expose it, `GET /v1/usage` has the charge.

```typescript
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-Price-Limit": "0.10" },
});

const { text } = await generateText({
  model: openfill.chatModel("market-preview"),
  prompt: "hi",
});
console.log(text);
```

## LangChain

`ChatOpenAI` with a base URL. LangChain's OpenAI class types the documented OpenAI response fields, so `usage.x_market` and `reasoning_content` may not reach its message object; check `response_metadata` for your version, or read them from the dashboard or the raw SDK.

```python
import os
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="market-preview",
    base_url="https://api.openfill.ai/v1",
    api_key=os.environ["OPENFILL_API_KEY"],
    default_headers={"X-Price-Limit": "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.

```python
import os
from litellm import completion

r = completion(
    model="openai/market-preview",
    api_base="https://api.openfill.ai/v1",
    api_key=os.environ["OPENFILL_API_KEY"],
    messages=[
        {"role": "user", "content": "hi"}
    ],
    extra_headers={"X-Price-Limit": "0.10"},
)
print(r.choices[0].message.content)
```

```yaml
# config.yaml, for litellm --config config.yaml
model_list:
  - model_name: market-preview
    litellm_params:
      model: openai/market-preview
      api_base: https://api.openfill.ai/v1
      api_key: os.environ/OPENFILL_API_KEY
      extra_headers:
        X-Price-Limit: "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.

```python
import os
from openai import AsyncOpenAI
from 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-Price-Limit": "0.10"},
))
set_default_openai_api("chat_completions")
set_tracing_disabled(True)

agent = Agent(name="assistant", model="market-preview")
print(Runner.run_sync(agent, "hi").final_output)
```

## Continue

A model block in `config.yaml` with the `openai` provider and an `apiBase`. The price limit rides in `requestOptions.headers`.

```yaml
models:
  - name: OpenFill
    provider: openai
    model: market-preview
    apiBase: https://api.openfill.ai/v1
    apiKey: ${{ secrets.OPENFILL_API_KEY }}
    requestOptions:
      headers:
        X-Price-Limit: "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 price limit comes from the key.

## Other tools

The same three values, in the field each tool has for them, and where the price limit goes. Read from each tool's own documentation; a tool with no header and no body field runs on the key or account default.

| Tool | Where the values go | Price limit |
| --- | --- | --- |
| OpenClaw | A provider in the models config with baseUrl ending in /v1, api openai-completions, apiKey, and a models list carrying contextWindow and maxTokens. | headers on the provider. |
| Hermes Agent | config.yaml: provider custom with base_url ending in /v1 and api_key. It reads GET /v1/models for context length. | extra_body under the provider. |
| Pi | ~/.pi/agent/models.json: a provider with baseUrl, api openai-completions, apiKey, and models with contextWindow and maxTokens. | headers on the provider. |
| Goose | A custom provider file with engine openai and base_url set to the full chat completions URL, or OPENAI_HOST with the built-in provider. | headers in the file, or OPENAI_CUSTOM_HEADERS. |
| Zed | settings.json: language_models.openai_compatible with api_url ending in /v1 and available_models with max_tokens as the context window. The key goes in the provider UI. | Key or account default. |
| JetBrains AI Assistant | Providers and API keys: the OpenAI-compatible provider with the URL, the key, and tool calling on. | Key or account default. |
| GitHub Copilot in VS Code | A custom endpoint provider with apiType chat-completions and a models list with toolCalling, maxInputTokens and maxOutputTokens. | Key or account default. |
| Xcode | Add a model provider with the URL without /v1, the key, and Authorization as the key header. Xcode adds the version segment itself. | Key or account default. |
| OpenHands | Settings, LLM, advanced: custom model openai/ and the model id, base URL, key. Or LLM_MODEL, LLM_BASE_URL and LLM_API_KEY. | Key or account default. |
| Qwen Code | settings.json modelProviders.openai with baseUrl, envKey and a generationConfig. | extra_body in generationConfig. |
| Factory Droid | customModels with provider generic-chat-completion-api, baseUrl and apiKey. The openai provider means the Responses API. | extraArgs or extraHeaders. |
| Warp | A custom inference endpoint in settings: the base URL, the key, the model ids. | Key or account default. |
| Jan | Add an OpenAI-compatible provider with the base URL ending in /v1 and the key; models are fetched from it. | Key or account default. |
| n8n | Base URL and key on the OpenAI credential; the OpenAI Chat Model node, with the model typed in. | Extra Body on the node, from n8n 2.34. |
| Dify | The OpenAI-API-compatible plugin: model name, key, endpoint URL, chat mode, and the context and token limits. | Key or account default. |
| Langflow | OPENAI_COMPATIBLE_BASE_URL and OPENAI_COMPATIBLE_API_KEY; models are discovered from GET /v1/models. | Key or account default. |
| DSPy | dspy.LM with the openai/ prefix, api_base and api_key, model_type chat. | extra_body as a keyword argument. |
| Instructor | from_provider with the openai/ prefix, base_url and api_key, mode TOOLS. | extra_body on the create call. |
| Agno | OpenAILike with id, api_key and base_url. | extra_body on the model. |
| Haystack | OpenAIChatGenerator with api_base_url and the key from the environment. | generation_kwargs with extra_body. |
| Spring AI | spring.ai.openai.base-url as the host, spring.ai.openai.api-key, spring.ai.openai.chat.model. The chat path is its own property. | spring.ai.openai.chat.extra-body.price_limit, or http-headers. |
| Mastra | A model object with url ending in /v1, apiKey and headers. | headers on the model. |
| AutoGen | OpenAIChatCompletionClient with base_url, api_key and a model_info block declaring function_calling. | default_headers. |
| Zapier, Make | No base URL on their OpenAI apps. Call the API from their HTTP action with the bearer header and price_limit in the body. | In the body. |

Two tools cannot be pointed here at all: Windsurf takes only an Anthropic key, and Amp has no custom endpoint. A tool whose cost display takes a fixed price per token cannot show a market price; leave those fields at zero and read the charge from `usage.x_market` or the requests page. On a fixed-price model those fields can hold its prices, from `GET /v1/models`.

## Gateways and routers

A gateway that takes an arbitrary OpenAI-compatible base URL reaches OpenFill today: Portkey through a custom host, Cloudflare AI Gateway through a custom provider, Bifrost, TrueFoundry, Kong, and the LiteLLM proxy above. One that routes only to the providers on its own list needs OpenFill listed first. Four things about a queued market decide whether a gateway serves you well.

| Behaviour | What it does to an order | What to set |
| --- | --- | --- |
| A retry on 408 | Each retry is a new order with a fresh wait. Nothing is charged for the expired ones, but the wait multiplies. An Idempotency-Key does not change this: an expired order releases its key. | Take 408 out of the retry list. |
| A fallback on 408 or 429 | The prompt goes to a different provider whenever an order's wait runs out while it is queued (408, or 429 capacity_unavailable on a fixed-price model), a locked request misses its window (429 capacity_unavailable), or a limit answers 429. | Take 408 and 429 out of the fallback triggers, or accept the fallback as your price ceiling. |
| The gateway's own timeout | A gateway that gives up first answers its own 408. If it drops its connection while the order is queued, the order is cancelled free; if it holds the connection, or the order has started, the order runs and is charged. | Keep max_wait under the gateway's timeout, and check the body for type order_expired (its code is null) before assuming an order is gone. |
| A body allowlist | Some gateways forward only fields they know, so price_limit, max_wait, price_lock and detached are dropped. A key can supply the price limit; the account supplies the timeout and price_lock defaults. detached needs a body field. | Use X-Price-Limit and X-Expires-After where headers pass through, set account defaults, or call OpenFill directly. |

A gateway that re-encodes the response hides `usage.x_market`; the charge is then on [GET /v1/usage](https://openfill.ai/docs/api/orders#usage) and the requests page. Detached orders, order endpoints and batches go to `https://api.openfill.ai` directly unless the gateway proxies whole paths. The base URL shape differs per gateway: some want `https://api.openfill.ai/v1`, some the host alone, and a wrong one answers `404` on the first call.

Previous: [Models](https://openfill.ai/docs/models). Next: [How pricing works](https://openfill.ai/docs/pricing).
