Every OpenAI-compatible client, configured

2026-06-17 · ~1500 words

Phantom is OpenAI-wire-compatible. Anything that already speaks https://api.openai.com/v1 works by switching base_url to https://phantom.codes/v1 and api_key to a Phantom key. This post is the copy-paste cheat sheet.

▸ The universal pattern

Every client below uses the same two values:

  • base URL: https://phantom.codes/v1
  • API key: the sk-... string you get back after payment

If a client takes those as environment variables, the canonical names are OPENAI_API_BASE (or OPENAI_BASE_URL in newer SDKs) and OPENAI_API_KEY. Set them once and most tools pick them up automatically.

▸ openai-python

from openai import OpenAI

client = OpenAI(
    base_url="https://phantom.codes/v1",
    api_key="sk-...",
)

resp = client.chat.completions.create(
    model="phantom/kimi-k2.6",
    messages=[{"role": "user", "content": "hello"}],
)
print(resp.choices[0].message.content)

Streaming is identical, set stream=True and iterate. Tool calling, function calling, vision, embeddings, image generation all forward unchanged.

▸ openai-node

import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://phantom.codes/v1",
  apiKey: process.env.OPENAI_API_KEY,
});

const resp = await client.chat.completions.create({
  model: "phantom/kimi-k2.6",
  messages: [{ role: "user", content: "hello" }],
});
console.log(resp.choices[0].message.content);

▸ LangChain (Python)

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="phantom/kimi-k2.6",
    base_url="https://phantom.codes/v1",
    api_key="sk-...",
)

print(llm.invoke("hello").content)

LangGraph picks up the same configuration through the underlying LangChain LLM. No additional setup.

▸ Cline (VS Code)

Cline is an autonomous coding agent extension for VS Code. In Cline's settings:

  • API Provider: OpenAI Compatible
  • Base URL: https://phantom.codes/v1
  • API Key: sk-...
  • Model ID: phantom/kimi-k2.6 (or any model from /models.html)

Pick a model with strong tool-calling support. For agentic coding, phantom/kimi-k2.6, phantom/qwen3-coder, or the proxy-tier phantom/claude-sonnet-4-5 work well.

▸ Aider

export OPENAI_API_BASE="https://phantom.codes/v1"
export OPENAI_API_KEY="sk-..."

aider --model openai/phantom/kimi-k2.6

Aider prefixes OpenAI-compatible models with openai/. The slash that follows is part of the Phantom model name.

▸ Continue (VS Code, JetBrains)

Continue's config.json (or config.yaml in newer versions) accepts an OpenAI-style provider block:

{
  "models": [
    {
      "title": "Phantom",
      "provider": "openai",
      "model": "phantom/kimi-k2.6",
      "apiBase": "https://phantom.codes/v1",
      "apiKey": "sk-..."
    }
  ]
}

▸ opencode

export OPENAI_API_BASE="https://phantom.codes/v1"
export OPENAI_API_KEY="sk-..."

opencode --model phantom/kimi-k2.6

▸ OpenHands (formerly OpenDevin)

export LLM_API_KEY="sk-..."
export LLM_BASE_URL="https://phantom.codes/v1"
export LLM_MODEL="openai/phantom/kimi-k2.6"

docker run -e LLM_API_KEY -e LLM_BASE_URL -e LLM_MODEL \
  -v $PWD:/workspace docker.all-hands.dev/all-hands-ai/openhands

OpenHands uses LiteLLM internally. The openai/ prefix routes through the OpenAI-compatible adapter.

▸ OpenWebUI

In OpenWebUI, go to Settings → Connections → OpenAI API:

  • API Base URL: https://phantom.codes/v1
  • API Key: sk-...

Models pull automatically from /v1/models. Pick one from the dropdown in chat. The Phantom catalog includes vision models, so OpenWebUI's image upload works against models that support it.

▸ LiteLLM

import litellm

resp = litellm.completion(
    model="openai/phantom/kimi-k2.6",
    api_base="https://phantom.codes/v1",
    api_key="sk-...",
    messages=[{"role": "user", "content": "hello"}],
)
print(resp.choices[0].message.content)

LiteLLM's proxy server config (litellm_config.yaml) uses the same fields under model_list[].litellm_params.

▸ Cursor

Cursor's API Keys settings allow an OpenAI override:

  • Go to Settings → Models → OpenAI API Key
  • Enable Override OpenAI Base URL
  • Base URL: https://phantom.codes/v1
  • API Key: sk-...

Cursor's hardcoded model list will not show Phantom-named models, so you may need to use a configured custom model or pick a model whose ID happens to match.

▸ Anything else with OPENAI_API_BASE

For any tool that respects the OpenAI SDK environment variables, the universal recipe is:

export OPENAI_API_BASE="https://phantom.codes/v1"
export OPENAI_BASE_URL="https://phantom.codes/v1"
export OPENAI_API_KEY="sk-..."

Set both OPENAI_API_BASE and OPENAI_BASE_URL because different SDK versions read different variable names. Setting both is harmless.

▸ Model naming

Phantom models are namespaced as phantom/<name>. Some clients require a vendor prefix in front of that (Aider, OpenHands, LiteLLM use openai/). Most don't.

The full catalog with live pricing is at /models.html or via GET /v1/models. The catalog includes:

  • TEE-attested open-weight: Kimi K2.6, Qwen3 Coder, DeepSeek V3.1, Llama 3.3 70B, Mistral, Gemma, and many more
  • Closed-weight proxy: Claude Sonnet 4.5, GPT-5, Gemini 2.5 Pro, Grok 4
  • Embeddings: multiple dimensions and models
  • Image generation: SDXL, Flux, and proxy-tier image models
  • Vision: models supporting image_url content parts

▸ What works and what doesn't

What works unchanged: chat completions, streaming, tool calling, function calling, vision (image_url content parts), embeddings, image generation, JSON mode (where the upstream supports it).

What's deliberately dropped at the proxy: user, metadata, logit_bias, logprobs, top_logprobs, seed. These are fingerprint-rich and not necessary for inference. The body whitelist is documented at /docs.html#endpoints.

What's not supported: OpenAI Assistants API (stateful threads, file storage on OpenAI's side), ChatGPT plugins, vendor-specific extensions outside the chat-completions surface.

▸ Verifying it works

Smoke test from a terminal:

curl https://phantom.codes/v1/chat/completions \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"phantom/kimi-k2.6","messages":[{"role":"user","content":"hi"}]}'

If you see a JSON response with choices[0].message.content, the integration is working. Per-request attestation is at /docs.html#attestation.

Remaining balance: curl -H "Authorization: Bearer $OPENAI_API_KEY" https://phantom.codes/v1/key/balance. Returns the remaining micro-USD credit.