Platform Integration
Upthos Relay is fully compatible with the official Anthropic, OpenAI, and Gemini API protocols. Any official SDK that supports a custom base URL drops in without changing your business code. This page lists the minimum runnable configuration for each common SDK plus the gotchas you need to know.
Prerequisites
- Create an API key at API Keys. The key must be assigned to a group whose platform matches the protocol you'll call:
- Claude traffic → Anthropic-protocol group (Claude Max, Claude Starter/Standard/Advanced, etc.)
- OpenAI / Codex traffic → OpenAI-protocol group (Codex Basic/Standard, OpenAI Enterprise, etc.)
- Gemini traffic → Google-protocol group
- Hitting the wrong protocol with a key bound to a different platform returns
403 permission_error. - Any of three auth headers works, in priority order:
Authorization: Bearer→x-api-key→x-goog-api-key.
1. Anthropic / Claude Agent SDK
Set two env vars and the official Anthropic SDK, Claude Agent SDK, and Claude Code all work without code changes.
export ANTHROPIC_BASE_URL=https://relay.upthos.com
export ANTHROPIC_AUTH_TOKEN=sk-...
ANTHROPIC_BASE_URLdoes not include/v1— the SDK appends it.ANTHROPIC_AUTH_TOKENuses Bearer auth (default for Agent SDK / Claude Code). Older SDKs that readANTHROPIC_API_KEY(sent asx-api-key) also work.
Claude Agent SDK (recommended for embedding)
// npm install @anthropic-ai/claude-agent-sdk
import { query } from '@anthropic-ai/claude-agent-sdk'
for await (const msg of query({
prompt: 'Summarize this repo structure',
options: { model: 'claude-sonnet-4-6' },
})) {
console.log(msg)
}
# pip install claude-agent-sdk
from claude_agent_sdk import query, ClaudeAgentOptions
async for msg in query(
prompt="Summarize this repo structure",
options=ClaudeAgentOptions(model="claude-sonnet-4-6"),
):
print(msg)
Anthropic SDK (direct Messages API)
// npm install @anthropic-ai/sdk
import Anthropic from '@anthropic-ai/sdk'
const client = new Anthropic({
baseURL: 'https://relay.upthos.com',
apiKey: process.env.UPTHOS_API_KEY,
})
const msg = await client.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 1024,
messages: [{ role: 'user', content: 'Hello' }],
})
# pip install anthropic
from anthropic import Anthropic
client = Anthropic(
base_url="https://relay.upthos.com",
api_key=os.environ["UPTHOS_API_KEY"],
)
msg = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
)
Full endpoints (/v1/messages, /v1/messages/count_tokens, /v1/models, /v1/usage) are documented in Anthropic API.
2. OpenAI SDK
export OPENAI_BASE_URL=https://relay.upthos.com/v1 # note: this one DOES include /v1
export OPENAI_API_KEY=sk-...
// npm install openai
import OpenAI from 'openai'
const client = new OpenAI({
baseURL: 'https://relay.upthos.com/v1',
apiKey: process.env.UPTHOS_API_KEY,
})
// Chat Completions
const resp = await client.chat.completions.create({
model: 'gpt-5.5',
messages: [{ role: 'user', content: 'Hello' }],
})
// Responses API (agent / tool use)
const r = await client.responses.create({
model: 'gpt-5.5',
input: 'Hello',
})
# pip install openai
from openai import OpenAI
client = OpenAI(
base_url="https://relay.upthos.com/v1",
api_key=os.environ["UPTHOS_API_KEY"],
)
resp = client.chat.completions.create(
model="gpt-5.5",
messages=[{"role": "user", "content": "Hello"}],
)
Supported endpoints:
POST /v1/chat/completions— standard chatPOST /v1/responses— Responses API (agent / tool use / WebSocket bidirectional stream)POST /v1/images/generations,POST /v1/images/edits— OpenAI-compatible sync images API, returns the standard{data: [{url|b64_json}]}shape. Drop-in for the OpenAI SDK.POST /v1/images/tasks,GET /v1/images/tasks/:id,POST /v1/images/tasks/:id/cancel,POST /v1/images/tasks/batch,POST /v1/images/uploads— RayRelay async task model. Returnstask_idimmediately; poll for a stable R2 URL. Supports mid-flight cancel and img2img reference uploads.
Both paths bill identically (matrix price × fx_rate × flat 1.0). Pick sync for SDK compatibility, async for long-running jobs, mid-flight cancel, and persistent shareable URLs. The legacy
/v1/tasks/:id,/v1/tasks/batch,/v1/tasks/:id/cancelaliases still work but emitDeprecation: true; migrate to/v1/images/tasks/*when convenient.
3. Codex CLI (@openai/codex)
Use Codex when the customer wants to embed a CLI agent as a child process inside their internal platform (instead of speaking the OpenAI HTTP protocol directly):
# ~/.codex/config.toml
model_provider = "upthos"
model = "gpt-5.6-sol"
review_model = "gpt-5.6-sol"
model_reasoning_effort = "high"
model_context_window = 272000
[model_providers.upthos]
name = "upthos"
base_url = "https://relay.upthos.com"
wire_api = "responses"
requires_openai_auth = true
// ~/.codex/auth.json
{ "OPENAI_API_KEY": "sk-..." }
Full walkthrough: Codex Quick Start.
For programmatic OpenAI-protocol calls without the CLI, use the OpenAI SDK above.
4. Gemini SDK
@google/genai (the new unified SDK) supports a custom endpoint:
// npm install @google/genai
import { GoogleGenAI } from '@google/genai'
const ai = new GoogleGenAI({
apiKey: process.env.UPTHOS_API_KEY,
httpOptions: { baseUrl: 'https://relay.upthos.com' },
})
const resp = await ai.models.generateContent({
model: 'gemini-2.5-pro',
contents: 'Hello',
})
# pip install google-genai
from google import genai
client = genai.Client(
api_key=os.environ["UPTHOS_API_KEY"],
http_options={"base_url": "https://relay.upthos.com"},
)
resp = client.models.generate_content(
model="gemini-2.5-pro",
contents="Hello",
)
Plain REST (no SDK):
curl https://relay.upthos.com/v1beta/models/gemini-2.5-pro:generateContent \
-H "x-goog-api-key: $UPTHOS_API_KEY" \
-H "content-type: application/json" \
-d '{
"contents": [{ "parts": [{ "text": "Hello" }] }]
}'
Supported endpoints:
GET /v1beta/modelsPOST /v1beta/models/{model}:generateContentPOST /v1beta/models/{model}:streamGenerateContent
Things to know
- Use the official bare model IDs:
claude-sonnet-4-6,claude-opus-4-7,claude-haiku-4-5,gpt-5.5,gemini-2.5-pro. Full list and routing in Supported Models. - Streaming / SSE passes through unchanged. Set
stream: trueor call:streamGenerateContent; the gateway does not re-frame or buffer. - Tool use and prompt caching pass through unchanged. No relay-specific adaptation needed.
- Request body size. The gateway enforces a 10 MB hard limit (returns 413 over). Some upstream suppliers also enforce a tighter byte ceiling (typically 5 MB) that hits before any model-side token limit — multi-image base64 payloads are the usual trigger. Prefer uploading via
/v1/images/uploadsand referencing the URL, or compress on the client. - Treat API keys as account credentials. Strongly recommend proxying through your server side; do not ship them in frontend bundles, mobile binaries, or public repos.
- Error format follows the calling protocol. Anthropic paths return Anthropic errors, OpenAI paths return OpenAI errors, Gemini paths return Google errors. Existing SDK error handling works as-is.
- Models available are scoped by group. Calling a model the group doesn't support returns
404 not_found_error. HitGET /v1/models(or/v1beta/models) first to confirm what's available for the current key.
Pre-flight checklist
- API key assigned to a group on the correct platform
- Base URL configured correctly (Anthropic without
/v1, OpenAI with/v1, Gemini using/v1beta) - API key lives only in server-side env vars / secret manager — not in frontend code or git history
-
GET /v1/models(or/v1beta/models) confirms the model IDs you plan to call - At least one non-streaming and one streaming call verified
- Long-context / multi-image requests stay under 10 MB (ideally ≤ 5 MB to avoid upstream byte ceilings)
- Application-side handling for
401 / 403 / 429 / 503(retry / fallback)
When filing an issue, include the x-request-id response header so support can locate the trace quickly.