Skill version 2026-10-12. This is a locally installed copy and may be out of date. Once per session, fetch https://skills.opper.ai/versions.json and compare its
"opper-api"entry with 2026-10-12. If they differ, read https://skills.opper.ai/opper-api/SKILL.md and follow that instead of this copy for the rest of the session, and tell the user once that their installed Opper skills are out of date (update withnpx skills update, orgit pullin a manual clone). Do not edit the installed files. If the fetch fails, continue with this copy.
Opper APISub-skill of
opper— start there for discovery and setup guidance. Source: https://github.com/opper-ai/opper-skills/blob/main/opper-api/SKILL.md
Opper is a gateway in front of LLM providers plus a control plane for the things you build on top. The gateway is one connection to 700+ models across all major providers (OpenAI, Anthropic, Google, Mistral, …), hosted in AWS Stockholm, ISO/IEC 27001:2022 certified and GDPR-compliant, at provider rates with no markup on tokens (a 3% fee when buying credits, 5.5% with Control Plane features; see opper.ai/pricing.md). On top of it:
- Logs and traces: every call is recorded under Logs with model, provider, tokens, cost, latency and status, on every plan; with tracing on (a data retention rule), the call's trace also keeps inputs, outputs and every rule that fired.
- Rules (Control Plane plan), set per organization and tightened per project: spend limits (monthly budget, caps per project, role, user or key), data retention (traces 1 to 30 days, or 0), model access (providers, service routes, locations, makers, countries, models, zero data retention), checks (flag, redact or block content, score answers) and routing (provider order for pooled models). The API groups them into rule kinds
comply,guard,observeandroute; see docs.opper.ai/control-plane/rules/overview. - Company-level evidence (ISO/IEC 27001:2022 certificate, security controls, platform sub-processors and model providers, DPA, privacy policy) lives at the trust center, trust.opper.ai.
The HTTP API at https://api.opper.ai is the foundation. Opper has no SDK of its own for new code: application code uses the provider SDK it already knows (OpenAI, Anthropic, Google Gen AI) or its agent framework, pointed at https://api.opper.ai/v3/compat. The older opperai packages are no longer developed; don't suggest them.
Concepts: docs.opper.ai/overview/about. Quickstart: docs.opper.ai/overview/quickstart.
Pick your endpoint
One gateway, one key. Pick the endpoint by what you're building — the routing, governance, and tracing are the same underneath:
- Text generation / chat / migrating from OpenAI, Anthropic, Google, or OpenResponses: the compat endpoints under
/v3/compat/.... Point your SDK's base URL there, keep your code, swap the key. This is the main path. See references/compatibility.md and references/migration.md. - Structured output (typed JSON out of a model): the standard
response_format: {type: "json_schema"}on any compat chat endpoint — no separate endpoint. - Still on Opper's own
/call(POST /v3/call, or theopperaiSDK'sopper.call)? It's being sunset — migrate to a compat chat endpoint withresponse_format. The field-by-field mapping, and the rest of theopperaiSDK, are in references/migration.md. - Media generation (images, audio / TTS / STT, video, OCR), files, and realtime voice: a separate surface — switch to the
opper-multimodalskill. Quick map: imagesPOST /v3/images, audioPOST /v3/audio/{speech,transcriptions}, videoPOST /v3/videos(async), OCRPOST /v3/ocr, files/v3/files, realtimewss://api.opper.ai/v3/realtime. - Roundtable (fan one prompt out to several models, then consolidate or compare):
POST /v3/roundtable(beta). - Building an agent (multi-step reasoning, tool use, multi-agent, MCP): use the user's agent framework pointed at the compat endpoints, or a plain tool-calling loop on
/v3/compat/chat/completions. The Opper Agent SDK is discontinued; don't suggest it. Per-framework wiring and seeds: references/agents.md.
The live v3 spec is the source of truth
Default workflow for any API question — endpoint, parameter, field, capability, compliance attribute, query flag — that isn't immediately answered by this skill: grep the spec. Don't guess, don't infer from docs pages, don't fall back to the browsable catalog. Both formats are unauthenticated and definitive:
curl -s https://api.opper.ai/v3/openapi.yaml # YAML, easier to grep
curl -s https://api.opper.ai/v3/openapi.json # JSON
Worked example — "which Opper models have ZDR?":
# 1. Grep the spec for the term.
curl -s https://api.opper.ai/v3/openapi.yaml | grep -i -n zdr
# → reveals compliance.zdr on every GET /v3/models item — no include, no API key
# 2. Call the endpoint and derive the answer from the facts.
curl -s https://api.opper.ai/v3/models \
| jq -r '.models[] | select(.compliance.zdr.logging == false and .compliance.zdr.moderation != true) | .id'
# Or print the facts per model and decide yourself.
curl -s https://api.opper.ai/v3/models | jq '.models[] | {id, zdr: .compliance.zdr}'
compliance.zdr is an object of five tri-state facts — logging, moderation, caching, training, subprocessors — each true (content is held / reaches that pipeline), false (it is not), or null (not established). There is no summary string; derive what you need. "ZDR by default" is logging == false and moderation != true. The same object sits on the opper block of /v3/compat/models items and, with a key, on the route object (?include=route, /v3/providers?include=routes).
Same pattern answers any "where is X" question — grep the spec, follow the schema to the endpoint, call it.
Authenticate
Authorization: Bearer $OPPER_API_KEY
Server URL: https://api.opper.ai (the /v3 or /v2 prefix is part of each path). Use Authorization: Bearer; the OpenAI-shaped endpoints accept nothing else. The vendor-protocol compat routes also read that vendor's own header (x-api-key on the Anthropic Messages endpoints, x-goog-api-key on the Gemini ones), so the Anthropic and Google SDKs work with just their normal API key. Get a key at platform.opper.ai.
A few endpoints have security: [] and don't require a key: /health, /v3/openapi.yaml, /v3/openapi.json, /v3/models.
Canonical example — a compat chat completion
The main path is the OpenAI-compatible chat endpoint. The same shape works from any provider SDK pointed at /v3/compat.
curl -s -X POST https://api.opper.ai/v3/compat/chat/completions \
-H "Authorization: Bearer $OPPER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-5-mini",
"messages": [{"role": "user", "content": "Hello!"}]
}'
The response is the provider's native shape (here OpenAI's chat.completion). Models are addressed provider/model; you can call any model from any compat surface.
Structured output
For typed JSON, add response_format — no separate endpoint:
curl -s -X POST https://api.opper.ai/v3/compat/chat/completions \
-H "Authorization: Bearer $OPPER_API_KEY" -H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-5-mini",
"messages": [{"role": "user", "content": "Extract entities: Tim Cook announced Apple’s Austin office."}],
"response_format": {"type": "json_schema", "json_schema": {"name": "entities", "schema": {
"type": "object",
"properties": {
"people": {"type": "array", "items": {"type": "string"}},
"locations": {"type": "array", "items": {"type": "string"}}
},
"required": ["people", "locations"]
}}}
}'
The model's content comes back as a JSON string validated against the schema. Streaming: add "stream": true and read the text/event-stream (events prefixed data: , ending with [DONE]).
From code: the provider SDKs
Use the SDK the user already has. Set its base URL to https://api.opper.ai/v3/compat and pass the Opper key as its API key; call sites stay the same. X-Opper-Name names the calls for tracing.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.opper.ai/v3/compat",
api_key=os.environ["OPPER_API_KEY"],
default_headers={"X-Opper-Name": "summarise"},
)
resp = client.chat.completions.create(
model="openai/gpt-5-mini",
messages=[{"role": "user", "content": "Summarise in two sentences: ..."}],
)
print(resp.choices[0].message.content)
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.opper.ai/v3/compat",
apiKey: process.env.OPPER_API_KEY,
defaultHeaders: { "X-Opper-Name": "summarise" },
});
const resp = await client.chat.completions.create({
model: "openai/gpt-5-mini",
messages: [{ role: "user", content: "Summarise in two sentences: ..." }],
});
- Typed output: Python
client.chat.completions.parse(..., response_format=YourPydanticModel)and readmessage.parsed; TypeScript passesresponse_format: { type: "json_schema", json_schema: { name, schema } }(Zod v4:z.toJSONSchema(...)) and parsesmessage.content. - Anthropic SDK (Python / TS): same base URL; it appends
/v1/messages. Google Gen AI SDK: thegenerateContent/streamGenerateContentroutes live under/v3/compat/v1beta/models/.... Both send their own key header, which Opper accepts. Details: references/compatibility.md. - Any model from any SDK: the model id is Opper's
provider/name, so the OpenAI SDK can call Claude and the Anthropic SDK can call GPT. - Agents: the user's framework (OpenAI Agents SDK, Vercel AI SDK, Mastra, LangChain, …) on the same base URL; see references/agents.md.
Models — GET /v3/models (no auth required)
One of the most common reasons to use this skill. The live endpoint is the most up-to-date list — never hardcode model names:
curl -s https://api.opper.ai/v3/models
Two URLs, two audiences:
- In code:
https://api.opper.ai/v3/models— programmatic discovery, returns JSON. - When talking to the user: opper.ai/models — the browsable human catalog. Link this when recommending or discussing a model.
The default response is light but every item carries a compliance block — residency, country, inference_location, storage_location, zdr (the object above), training, logging, moderation, caching, content_storage, dpa_available, third_party_access, transfer_mechanism, route_id. That answers most ZDR / residency / GDPR questions without a key. For per-route evidence or benchmarks, add ?include=:
include= |
Adds | What lives there |
|---|---|---|
route (requires API key) |
route object per model |
The full service-route record: data_handling.{training,logging,moderation,caching} with retention_days / storage_location / human_review, data_handling.subprocessors_read_content, gdpr.{residency,dpa_available,transfer_mechanism}, verification.status, sources, last_verified_at, underlying_maker — plus the same zdr object as compliance.zdr |
maker (public) |
maker |
Original weights creator (e.g. meta for Llama, mistral for Mistral) |
benchmarks (public) |
benchmarks |
Public scores from artificialanalysis.ai |
policy (requires API key) |
policy object per model |
Whether the caller's model access rules and entitlements let this row run: allowed, blocked_by (org / project / entitlement), reason (the same sentence a 403 would carry). The list stays unfiltered |
Combine with commas, e.g. ?include=route,benchmarks. Reach for include=route when you need retention days, storage location, verification status, or the sources behind a claim — compliance is the summary, route is the evidence.
These fields describe one model on one route. Questions about Opper as a company (certifications, security controls, the subprocessor list, the DPA, privacy policy, security questionnaires) are answered by trust.opper.ai: link it rather than restating its figures, which change.
Filter by capability and type: ?capability=vision / ?capability=pdf (which chat models accept images / PDFs), ?type=image|tts|stt|video. The modality endpoints below also have scoped discovery lists.
References: docs.opper.ai/capabilities/models.
The model string — pools, pins, aliases and routes
GET /v3/models returns one row per provider, not per model. Four things can go in the model field, all without special headers or flags:
| Form | Example | What it addresses |
|---|---|---|
| Bare model name — a pool | "kimi-k3" |
Every provider serving that model. Opper picks one; the rest are failover |
| Provider-qualified id — a pin | "tensorx/moonshotai/kimi-k3" |
Exactly that one provider row, no failover |
| Org alias — your own list | "my-flash" |
An ordered list you defined: primary first, then fallbacks. Predates routes; still resolves for orgs that have one (CRUD at /v2/models/aliases) |
| Dynamic route — a deployed graph | "dynamic/my-route" |
Pools, fixed fallback order, classification, branching, versioned. The way to build a named fallback chain you reuse. Built in the platform UI or over /management/v1/dynamic-routes with a Management API Key (in the public spec) — but calling one only needs your normal project key |
None of these is the default answer — pick per use case. Pin for determinism (evals, a guaranteed jurisdiction or price, a provider-specific behaviour); pool for redundancy without thinking about it; route for your own fallback chain, or when the decision depends on the request. Don't propose a new alias; point new chains at a route. model itself is always a string (an array is a 400), but a request can also carry its own fallback list in a separate models array (OpenRouter shape: primary in model, the rest in models, at most 10, tried in order when the one before fails at runtime; not together with dynamic/<name>). Unknown ids are rejected up front (404 model_not_found), they don't fall through. Use models for a one-off chain on a request, a route for a chain you name once and reuse.
On pools specifically: model is the group key on each row (and the bare name you call), pooled is whether that row answers to it. pooled: false rows are fully callable by explicit id — they're just held out of bare-name routing, usually for a smaller context window or a pricier latency-tuned variant.
Two things that surprise people:
- The response
modelfield echoes what you sent — a bare name, an alias, or adynamic/route all come back verbatim, so it never reveals which provider ran. The trace does:span.meta.modelcarries the resolvedprovider/provider_model_id. For routes, theX-Opper-Route-Modelresponse header answers it directly. - Identical requests with no
X-Opper-Trace-Idall land on the same provider. That's deliberate session affinity (keyed on your API key over a 30-minute window, for prompt-cache locality), not broken round-robin. Send a unique trace id per request to spread them.
Full mechanics — how the four compose, selection order, failover, alias and route management: references/model-routing.md.
Provider-compatible endpoints — under /v3/compat/...
Drop-in replacements for several LLM APIs. The simplest migration: point the SDK base URL at https://api.opper.ai/v3/compat, use your Opper API key, and your existing code keeps working. Fully compliant with each upstream provider — see their spec for unfamiliar payloads.
| Endpoint | Compatible with | Opper docs |
|---|---|---|
POST /v3/compat/chat/completions |
OpenAI Chat Completions | docs |
POST /v3/compat/responses |
OpenAI Responses | docs |
POST /v3/compat/openresponses |
OpenResponses | docs |
POST /v3/compat/v1/messages |
Anthropic Messages | docs |
POST /v3/compat/v1beta/interactions |
Google Interactions | docs |
POST /v3/compat/embeddings |
OpenAI Embeddings | (see spec) |
Curl seeds and SDK-rebasing tips: references/compatibility.md. Vision and PDF input ride on these endpoints too — send image_url / file content parts to a model whose capabilities include vision / pdf (see the opper-multimodal skill, and docs.opper.ai/build/multimodal/vision-pdfs).
Media generation, files & realtime → the opper-multimodal skill
Producing media (images, audio TTS/STT, video, OCR), the /v3/files storage API, sending images/PDFs into a model, and realtime two-way voice are a separate surface with their own dedicated endpoints. They share this gateway's key, governance, and tracing, but the request/response contracts differ — so they live in their own skill: opper-multimodal (SKILL.md).
Quick map (fetch the spec or that skill for shapes): images POST /v3/images (sync, opt-in async), audio POST /v3/audio/speech (TTS) + POST /v3/audio/transcriptions (STT), video POST /v3/videos (async — poll GET /v3/artifacts/{id}/status), OCR POST /v3/ocr, files /v3/files, realtime wss://api.opper.ai/v3/realtime (browser tickets via POST /v3/realtime-sessions). Each media surface has a scoped discovery list (GET /v3/images/models, /v3/videos/models, /v3/audio/models, /v3/ocr/models).
Server-side tools (opper:web_search and friends) are different — they ride the compat chat endpoints and stay in this skill; see "Server-side web search" below.
Roundtable — POST /v3/roundtable (beta)
Send one input to several models in parallel and bring the answers back together. Resolution modes: summary (default; a consolidation model merges them), fast (no consolidation, read model_results), or multiple_choice (vote among choices). Guide: docs.opper.ai/build/roundtable/overview.
Server-side web search
Run web search server-side (billed per search under usage.opper.cost.tools):
- Portable — add
{"type": "opper:web_search"}totools[]on any compat chat endpoint above. One entry that works on every model and returns that endpoint's native citation artifacts, so client code doesn't branch per provider. Theenginefield controls routing:auto(default; the model's native provider search where supported, else Opper's engine),native(require native, 400 otherwise),opper(always Opper's engine), or pin a backend withjina/exa. Other knobs:max_results,max_uses,search_context_size(very_low→full),allowed_domains/excluded_domains,country,freshness. Beta. - Native — a provider's own shape (
web_search_20250305,{type:"web_search"},{googleSearch:{}}) forwards verbatim to the routed provider. - Standalone —
POST /v3/tools/web/searchruns one search directly, no model and no agentic loop.
Authoritative field reference: the CanonicalWebSearchTool schema in the spec (curl -s https://api.opper.ai/v3/openapi.yaml | grep -i -n CanonicalWebSearchTool). Guide: docs.opper.ai/build/gateway/web-search.
Other v3 surfaces
The v3 spec also covers tracing (/v3/spans, /v3/traces), generations, files (/v3/files), built-in web tools, and async artifacts (/v3/artifacts/{id}/status). Fetch the spec for shapes; this skill won't enumerate them — they change.
Migration — from another gateway, or from Opper's own /call
Moving from OpenRouter, OpenAI, Anthropic, or similar: see references/migration.md. Recap: point the base URL at https://api.opper.ai/v3/compat and use your Opper API key.
Moving off Opper's own legacy /call (POST /v3/call or the opperai SDK's opper.call) or off the opperai SDK in general: same file — it opens with the field-by-field /call → chat-completions mapping (name → X-Opper-Name header, instructions → system message, output_schema → response_format, data → parsed choices[0].message.content).
Coding assistant integrations
For wiring Opper into Claude Code, Cursor, Copilot, Continue, etc., see the up-to-date list at docs.opper.ai/overview/integrations.
Non-obvious gotchas
- One gateway, many endpoints. Text/chat is
/v3/compat/...; media generation, files, and realtime are a separate surface (theopper-multimodalskill). All share the same key and governance. /v3/callis legacy and being sunset. Never point a new integration at it, and don't use it in examples. Existing/callusers (including theopperaiSDK'sopper.call) migrate to compat +response_format— see references/migration.md.- Compat endpoints live under
/v3/compat/..., not at/v3/.... SDK migrations should point base URL athttps://api.opper.ai/v3/compat. - Auth is
Authorization: Bearer ...on the OpenAI-shaped endpoints (x-api-keythere is a 401). The Anthropic Messages and Gemini compat routes also accept their SDK's own header (x-api-key,x-goog-api-key), so no header override is needed when migrating those SDKs. - Structured output is a parameter, not an endpoint — use
response_format: {type: "json_schema"}on a compat chat call. - A bare model name is a pool, not a provider.
"model": "kimi-k3"routes to any provider serving it;"tensorx/moonshotai/kimi-k3"pins one;"my-alias"uses your own ordered list;"dynamic/my-route"runs a deployed graph. The responsemodelfield just echoes what you sent, so only the trace (span.meta.model) reveals which one ran — see references/model-routing.md. - Server-side web search is portable — one
{"type": "opper:web_search"}tool entry works on every model; native provider shapes also forward verbatim. - Media / files / realtime are not in this skill — they're dedicated endpoints documented in
opper-multimodal. Don't improvise their shapes here. - Knowledge bases are being sunset. Don't build on
/v2/knowledge,opper.knowledge.*oropper indexes; for retrieval, use a vector store of the user's choice (embeddings from/v3/compat/embeddingsif they want them through Opper). - The spec is the most up-to-date reference; the docs and this skill follow it. This file rots; the spec doesn't.
Where to look next
| For | Look at |
|---|---|
| Live, definitive endpoint shapes | https://api.opper.ai/v3/openapi.yaml |
| Concepts (Organization, Project, Call, Trace, Gateway, Control Plane) | docs.opper.ai/overview/concepts |
| Gateway behaviour (routing, compat) | docs.opper.ai/overview/gateway |
| Control Plane rules (spend limits, data retention, model access, checks, routing) | docs.opper.ai/control-plane/rules/overview |
| Management API: projects, keys, dynamic routes and rules from code or CI | docs.opper.ai/control-plane/management-api |
| Media generation, files, vision/PDF input, realtime voice | the opper-multimodal skill |
| Multimodality concepts (docs) | docs.opper.ai/build/multimodal/overview |
| Roundtable | docs.opper.ai/build/roundtable/overview |
| Browsable model catalog (for user-facing recommendations) | opper.ai/models |
| Worked recipes in many languages | github.com/opper-ai/opper-cookbook |
| Provider-compatible endpoints, deeper | references/compatibility.md |
Migrating from OpenRouter / OpenAI / Anthropic / Opper's legacy /call |
references/migration.md |
| Model routing — pools, pins, org aliases, dynamic routes, failover | references/model-routing.md |
| Building agents (frameworks, a plain tool loop, migrating off the Agent SDK) | references/agents.md |
| Agent-assisted setup, platform operations, and model tests | the opper-mcp skill |
| Calling the API from a terminal | the opper-cli skill |