API reference

Documentation

OmniAPI speaks two protocols on the same host: the OpenAI chat-completions format, and Anthropic's messages format. One key works on both, spends the same wallet, and counts against the same limits — pick whichever one your tool already knows how to talk.

Start here

  1. 1. Create an account and open Dashboard → API keys.
  2. 2. Generate a key. Copy it — it is shown once and stored only as a hash.
  3. 3. Pick the base URL that matches your client and export both values.
export OMNI_API_KEY="omni_live_xxxxxxxxxxxx"

# OpenAI-format clients (Codex, Cline, OpenAI SDKs, curl)
export OMNI_BASE_URL="https://omniapi-ashy.vercel.app/api/v1"

# Anthropic-format clients (Claude Code, Anthropic SDKs)
export ANTHROPIC_BASE_URL="https://omniapi-ashy.vercel.app/api/anthropic"

Base URLs

Same host, same key, same balance. The only difference is the shape of the JSON on the wire — which is decided by your client, not by you.

OpenAI format

https://omniapi-ashy.vercel.app/api/v1

POST /chat/completions, GET /models. Use it for Codex CLI, Cline, Roo Code, Continue, LiteLLM, LangChain, and the OpenAI SDK in any language.

Anthropic format

https://omniapi-ashy.vercel.app/api/anthropic

POST /v1/messages, POST /v1/messages/count_tokens, GET /v1/models. Use it for Claude Code and the Anthropic SDKs.

Why two and not one: Claude Code does not speak chat-completions. It sends POST /v1/messages with a different request body, a different streaming event format and a different error shape. A single OpenAI-only endpoint cannot serve it, so the gateway translates between the two formats in both directions and bills the result identically either way.

Authentication

Send your key as a bearer token. x-api-key is also accepted, which is what the Anthropic SDKs send by default — so both surfaces work with whichever header your client prefers.

Authorization: Bearer omni_live_xxxxxxxxxxxx
# or
x-api-key: omni_live_xxxxxxxxxxxx

Keys are stored as SHA-256 hashes. A lost key cannot be recovered, only revoked and replaced. Treat a key like a password: it can spend your balance.

Claude Code

Two environment variables and nothing else. ANTHROPIC_AUTH_TOKEN carries your OmniAPI key; Claude Code sends it as a bearer token, which this gateway accepts.

export ANTHROPIC_BASE_URL="https://omniapi-ashy.vercel.app/api/anthropic"
export ANTHROPIC_AUTH_TOKEN="$OMNI_API_KEY"

claude

Put those two lines in your ~/.bashrc, ~/.zshrc or PowerShell profile to make it permanent. On Windows PowerShell:

$env:ANTHROPIC_BASE_URL = "https://omniapi-ashy.vercel.app/api/anthropic"
$env:ANTHROPIC_AUTH_TOKEN = "omni_live_xxxxxxxxxxxx"

claude

Claude Code asks for models by their Anthropic names — claude-opus-*, claude-sonnet-*, claude-haiku-*. Those resolve to the matching route automatically, so you do not have to change the model setting. To pin one explicitly, set ANTHROPIC_MODEL to any route or alias below.

If you already have ANTHROPIC_API_KEY set for a direct Anthropic account, unset it — Claude Code prefers it over the auth token and your requests will go to Anthropic instead of here.

Codex CLI

Codex reads providers from ~/.codex/config.toml. Add OmniAPI as one and select it. wire_api = "chat" matters: it tells Codex to use chat-completions rather than the Responses API.

# ~/.codex/config.toml
model = "gpt-5-codex"
model_provider = "omniapi"

[model_providers.omniapi]
name = "OmniAPI"
base_url = "https://omniapi-ashy.vercel.app/api/v1"
env_key = "OMNI_API_KEY"
wire_api = "chat"

Then export the key in the same shell you run codex from — the env_key line above is what tells Codex which variable to read.

export OMNI_API_KEY="omni_live_xxxxxxxxxxxx"
codex

Cline, Roo Code, Continue

All three have an OpenAI Compatible provider. The settings are always the same three fields:

ProviderOpenAI Compatible
Base URLhttps://omniapi-ashy.vercel.app/api/v1
API keyomni_live_xxxxxxxxxxxx
Model IDomni-pro (or any alias below)

Continue uses a YAML config instead of a form:

models:
  - name: OmniAPI
    provider: openai
    model: omni-pro
    apiBase: https://omniapi-ashy.vercel.app/api/v1
    apiKey: omni_live_xxxxxxxxxxxx

SDKs & curl

OpenAI SDK (Python)

from openai import OpenAI

client = OpenAI(
    api_key="omni_live_xxxxxxxxxxxx",
    base_url="https://omniapi-ashy.vercel.app/api/v1",
)

resp = client.chat.completions.create(
    model="omni-pro",
    messages=[{"role": "user", "content": "Explain rolling windows."}],
)
print(resp.choices[0].message.content)

OpenAI SDK (Node)

import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.OMNI_API_KEY,
  baseURL: "https://omniapi-ashy.vercel.app/api/v1",
});

const resp = await client.chat.completions.create({
  model: "omni-pro",
  messages: [{ role: "user", content: "Explain rolling windows." }],
});
console.log(resp.choices[0].message.content);

Anthropic SDK (Python)

from anthropic import Anthropic

client = Anthropic(
    api_key="omni_live_xxxxxxxxxxxx",
    base_url="https://omniapi-ashy.vercel.app/api/anthropic",
)

msg = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Explain rolling windows."}],
)
print(msg.content[0].text)

curl — OpenAI format

curl "https://omniapi-ashy.vercel.app/api/v1/chat/completions" \
  -H "Authorization: Bearer $OMNI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "omni-pro",
    "messages": [
      {"role": "system", "content": "You are concise."},
      {"role": "user",   "content": "Explain rolling windows."}
    ],
    "temperature": 0.4
  }'

curl — Anthropic format

curl "https://omniapi-ashy.vercel.app/api/anthropic/v1/messages" \
  -H "x-api-key: $OMNI_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4-5",
    "max_tokens": 1024,
    "messages": [
      {"role": "user", "content": "Explain rolling windows."}
    ]
  }'

Response (OpenAI format)

{
  "id": "chatcmpl-...",
  "object": "chat.completion",
  "model": "omni-pro",
  "choices": [
    { "index": 0,
      "message": {"role": "assistant", "content": "..."},
      "finish_reason": "stop" }
  ],
  "usage": {
    "prompt_tokens": 42,
    "completion_tokens": 118,
    "total_tokens": 160
  }
}

Models & aliases

Seven routes, priced per million tokens. GET /api/v1/models returns them in OpenAI's shape with pricing; GET /api/anthropic/v1/models returns the same list in Anthropic's shape.

RouteFamilyContextIn / 1MOut / 1M
omni-maxFrontier200K$30$150
omni-proFrontier200K$6$30
omni-fastBalanced128K$1.6$8
omni-miniEconomy128K$0.5$2.5
omni-visionMultimodal128K$5$20
omni-embedEmbeddings8K$0.04$0
omni-codeSpecialist256K$3$15

Provider names work too

Coding agents send the model name they know, not ours. Those are matched by pattern and resolved to a route, so a model released next month still works instead of returning a 404. Billing always uses the route it resolved to — asking for a cheap-sounding name never gets you an expensive model at the cheap price.

You sendRoutes to
claude-opus-4-6omni-max
claude-sonnet-4-6omni-pro
claude-haiku-4-5omni-fast
gpt-5omni-pro
gpt-5-codexomni-code
gpt-5-miniomni-fast
gemini-2.5-proomni-pro

The rule behind the table: opusomni-max, sonnet omni-pro, haiku / mini / nano / flash omni-fast, codexomni-code, anything with embed in it → omni-embed. Everything else lands on omni-pro.

Streaming

Set "stream": true on either surface. Tokens are relayed as they arrive, and the response is metered exactly once when the stream ends — including when you cancel it half way, so a stopped generation still bills for what was already produced.

OpenAI format

data: {"choices":[{"delta":{"content":"Rolling"}}]}
data: {"choices":[{"delta":{"content":" windows"}}]}
data: {"choices":[{"delta":{},"finish_reason":"stop"}],"usage":{...}}
data: [DONE]

Anthropic format

event: message_start
data: {"type":"message_start","message":{...}}

event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Rolling"}}

event: message_stop
data: {"type":"message_stop"}

Tool calls stream too, in whichever dialect you asked for — arguments arrive as tool_calls[].function.arguments fragments on the OpenAI side and input_json_delta blocks on the Anthropic side.

Limits & headers

Two things gate a request: your wallet must have credit, and your plan's rolling 5-hour and 7-day spend allowances must have room. Both are checked before anything is forwarded upstream, and a rejected request is never billed — neither is a request that the upstream provider fails.

x-omni-balance-usd

Wallet credit remaining after this request was charged.

x-omni-cost-usd

What this single request cost you.

x-omni-window-5h-remaining

USD left in the 5-hour window, returned on every response.

x-omni-window-7d-remaining

USD left in the weekly window, returned on every response.

Windows are rate limits, not an allowance of free money — real spend always comes out of the wallet. See pricing for the per-plan figures.

Errors

StatusCodeMeaning
401invalid_api_keyKey missing, malformed, or revoked.
402insufficient_balanceWallet credit is exhausted.
404model_not_foundNo route matched that model name.
429window_exceeded5-hour or weekly allowance used up.
502upstream_errorThe upstream provider failed. Not billed.
503not_configuredNo upstream is configured for that route.

On /api/v1 errors come back in OpenAI's shape:

{
  "error": {
    "type": "window_exceeded",
    "message": "5-hour usage window exhausted.",
    "window": "5h",
    "retry_after_seconds": 4820
  }
}

On /api/anthropic they come back in Anthropic's shape, so SDK error handling keeps working:

{
  "type": "error",
  "error": {
    "type": "window_exceeded",
    "message": "5-hour usage window exhausted."
  }
}

Something not covered here? Get in touch.

Documentation | OmniAPI