Get started in 5 minutes. One URL change — your existing OpenAI code works unchanged.
On this page
Install the OpenAI SDK, swap the base_url, and you're routing through Viapi. Your API key is issued by your firm admin in the dashboard.
Install
pip install openai
Python
from openai import OpenAI
client = OpenAI(
api_key="vi_live_your_key_here",
base_url="https://api.viapi.ai/v1/proxy"
)
response = client.chat.completions.create(
model="default", # Routes to your configured provider
messages=[{"role": "user", "content": "Hello!"}]
)
print(response.choices[0].message.content)Viapi accepts your API key in two ways. Keys are prefixed vi_live_ and are scoped to your client account.
X-Viapi-Key: vi_live_your_key_here
Authorization: Bearer vi_live_your_key_here
Your firm admin creates and issues API keys from the Viapi dashboard. Keys can be scoped with budget limits and rate caps per client.
Base URL: https://api.viapi.ai
/v1/proxy/chat/completionsChat completions — supports batch and streaming (SSE)/v1/proxy/modelsList available models for your account/v1/dashboard/client/me/usageYour usage statistics and token counts/v1/export/usageExport usage as CSV for billing reconciliation/v1/proxy/images/generationsImage generation — DALL-E and other image models/v1/video/jobsSubmit async video generation job, returns viapi_job_id/v1/video/jobs/{id}Poll video job status and get result URL on completion/v1/estimateEstimate cost in cents before generating — informational onlyFull interactive reference: Swagger UI ↗
Pass stream=Trueto receive server-sent events (SSE). The response format matches OpenAI's streaming protocol exactly.
stream = client.chat.completions.create(
model="default",
messages=[{"role": "user", "content": "Hello!"}],
stream=True
)
for chunk in stream:
print(chunk.choices[0].delta.content, end="")Responses are OpenAI-compatible with an additional viapi extension block containing billing metadata — cost, markup, and provider routing information.
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1714000000,
"model": "gpt-4o",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I help you today?"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 18,
"total_tokens": 30
},
"viapi": {
"request_id": "req_01jwxyz",
"client_id": "cli_abc",
"provider": "openai",
"model_used": "gpt-4o",
"cost_usd": 0.000042,
"markup_usd": 0.000013,
"billed_usd": 0.000055
}
}viapi.request_idUnique request identifier for support
viapi.providerWhich upstream provider handled the request
viapi.cost_usdRaw provider cost before markup
viapi.billed_usdAmount billed to this client
Rate limit status is returned in response headers. When limits are exceeded, the API returns 429 Too Many Requests.
X-RateLimit-Limit-RPM60Requests per minute limitX-RateLimit-Remaining-RPM—Requests remaining this minuteX-RateLimit-Limit-RPD10,000Requests per day limitX-RateLimit-Remaining-RPD—Requests remaining todayNote: Limits are set per client by your firm admin. Defaults above apply if no custom limit is configured. Contact your admin to request increased limits.
Route image generation through Viapi for metered billing. Uses the OpenAI SDK's images.generate method — same base_url swap, no other changes.
from openai import OpenAI
client = OpenAI(
api_key="vi_live_your_key_here",
base_url="https://api.viapi.ai/v1/proxy"
)
response = client.images.generate(
model="dall-e-3",
prompt="A photorealistic sunset over the ocean",
n=1,
size="1024x1024",
quality="hd",
)
print(response.data[0].url)
# viapi billing: response.viapi.billed_usdVideo generation is asynchronous. Submit a job and poll /v1/video/jobs/{id} until completed. Credits are deducted at submission and reconciled to actual cost on completion.
import httpx, time
headers = {"X-Viapi-Key": "vi_live_your_key_here"}
base = "https://api.viapi.ai"
# Submit job
job = httpx.post(f"{base}/v1/video/jobs", headers=headers, json={
"model": "runway-gen3-alpha",
"prompt": "A cinematic drone shot over a city at dusk",
"duration_seconds": 5,
}).json()
job_id = job["viapi_job_id"]
# Poll until complete
while True:
result = httpx.get(f"{base}/v1/video/jobs/{job_id}", headers=headers).json()
if result["status"] == "completed":
print(result["result_url"])
break
elif result["status"] == "failed":
raise RuntimeError("Job failed")
time.sleep(5)Get a cost estimate before generating — useful for showing clients a price preview in your UI. Estimates are informational; actual billed amount may differ slightly for video (reconciled on completion).
import httpx
resp = httpx.post(
"https://api.viapi.ai/v1/estimate",
headers={"X-Viapi-Key": "vi_live_your_key_here"},
json={
"model": "dall-e-3",
"n": 1,
"size": "1024x1024",
"quality": "hd",
},
).json()
cost_usd = resp["estimated_cost_cents"] / 100
print(f"Estimated cost: ${cost_usd:.4f}")
# {"estimated_cost_cents": 8, "provider": "openai",
# "breakdown": {"provider_cost_cents": 8, "markup_cents": 0}}The Viapi MCP server exposes the control plane as native tools in Claude Code and other MCP-compatible agent CLIs. Connect once and manage clients, mint keys, and list models in plain English — no API calls, no copy-pasting IDs.
claude mcp add viapi --transport http https://api.viapi.ai/mcp
Must use --transport http. On next startup Claude Code shows an Authenticate button — click it, sign in with your firm admin account, done.
Use the Authenticate button in Claude Code. A browser window opens, you sign in, and the token is stored permanently. No manual token management.
Account requirement: Use a firm admin account (e.g. dawn.le@rennlabs.com). Platform admin accounts are rejected.
# Step 1: Get a Supabase JWT from the dashboard
# DevTools → Application → Local Storage → access_token
# Step 2: Mint a PAT
curl -s -X POST https://api.viapi.ai/v1/auth/tokens \
-H "Authorization: Bearer <supabase-jwt>" \
-H "Content-Type: application/json" \
-d '{"label": "claude-code-mcp"}' | jq .
# Step 3: Register with Claude Code
claude mcp add viapi \
-e VIAPI_API_URL=https://api.viapi.ai \
-e VIAPI_ADMIN_TOKEN=vi_pat_<your-token> \
-- python3 -m viapi_mcp.serverlist_modelsList available models with context window size and ZDR capabilitylist_clientsList all client accounts under your firmget_clientGet a single client's details by IDcreate_clientCreate a new client accountupdate_clientUpdate a client's name or contact detailslist_keysList active API keys for a client (prefix, caps, allowlists)revoke_keyPermanently revoke a key — requires confirm=true to executeupdate_keyUpdate guardrails on an existing key (cap, enforcement, model allowlist) — cap clamped at $500/monthlist_routesList model routes for a client (alias → provider:model mappings)create_routeAdd a model route for a client with optional fallbackget_spending_limitGet the client-level monthly spending limit and alert thresholdsset_spending_limitSet a client-level monthly spending cap — clamped at $500/month server-sideget_usageGet monthly cost, token, and request counts for a clientget_credit_balanceGet current prepaid credit balance (in cents) and auto-reload settings for a clientcreate_billing_portal_urlGenerate a one-time Stripe Checkout URL so a client can add their payment card (expires 24 h)mint_guardrailed_keyCreate a vi_live_ key with spend cap, model allowlist, optional expiry — monthly limit capped at $500 server-side# Plain English — Claude calls the right tool automatically list my viapi clients create a client called "Acme Corp", slug acme-corp, email ops@acme.com mint a key for client <id> with a $100/month cap, allow dall-e-3 and gpt-4o-mini list available models that support ZDR
Check the full Swagger reference or reach out to your firm admin.