API reference
The SwitchGate REST API, endpoint by endpoint. OpenAI-compatible at https://api.switchgate.ai/v1 - request and response shapes are stable for the public beta; fields marked preview may evolve. Prefer prose? Start with the guides, or grab the OpenAPI 3.1 spec.
- Authentication
- Conventions
- Chat completions
- Models
- Embeddings
- API keys
- Usage & ledger
- Errors
- Headers & limits
Authentication
All requests are authenticated with a bearer key against the base URL https://api.switchgate.ai/v1. Keys are created in the console (or via the keys API), belong to a named user, and carry that user's budgets and model locks. The API is OpenAI-compatible: official OpenAI SDKs work by changing base_url.
curl https://api.switchgate.ai/v1/models \
-H "Authorization: Bearer sg-live-…"
Conventions
Base URL & versioning. All endpoints live under https://api.switchgate.ai/v1. The v1 surface is stable; additive fields may appear and are never breaking. Deprecations get 12 months' notice on the changelog.
Key types. sg-live-… keys call inference endpoints and read their own usage. sg-admin-… keys additionally manage keys, tunnels and org-wide usage. Admin keys cannot be created via the API - console only, on purpose.
Idempotency. Send an idempotency-key header (any string ≤255 chars, unique per operation) on POSTs; retries within 24h return the original result instead of acting twice.
Pagination. List endpoints accept limit (default 50, max 200) and after (an object ID); responses include has_more and the last ID as next_after.
Machine-readable spec. The full surface is described in openapi.json (OpenAPI 3.1) - import it into Postman, Bruno or your codegen of choice.
POST/v1/chat/completions
Create a model response for a conversation. Works with every model in the catalog, switchgate/auto, and your own local/ or private/ models via the Model Tunnel.
| Parameter | Type | Description |
|---|---|---|
modelrequired | string | Catalog ID like anthropic/claude-sonnet-4-6, switchgate/auto, or local/<model>. |
messagesrequired | array | Chat messages, OpenAI format: [{"role":"user","content":"…"}]. Multimodal content parts supported where the model supports them. |
stream | boolean | Server-sent events when true. Final chunk includes usage and exact cost. |
temperature | number | 0 - 2, passed through to the provider. Provider defaults apply when omitted. |
max_tokens | integer | Output cap, passed through. |
tools | array | Tool definitions, OpenAI format; forwarded to models with tool support. |
tool_choice | string | object | auto, none, required, or a specific tool. |
response_format | object | {"type":"json_object"} or {"type":"json_schema","json_schema":{…}} where the model supports structured output. |
top_p | number | Nucleus sampling, passed through. |
stop | string | array | Up to 4 stop sequences. |
frequency_penalty / presence_penalty | number | -2 to 2, passed through where supported. |
seed | integer | Best-effort determinism on providers that honor it. |
user | string | Your end-user identifier; appears on the ledger for per-customer accounting. |
sg_pii_redaction | string | Per-request override of the key's PII policy: may only be stricter (mask → block), never looser. |
sg_cache | string | "auto" (default, exact-match), "semantic" (paraphrase matching, key must opt in) or "off". Hits bill 0 credits. |
sg_fallbacks | array | Ordered model IDs to fail over to on provider error or timeout, e.g. ["openai/gpt-5.2","google/gemini-3-pro"]. |
metadata | object | Up to 8 string key-values attached to the ledger trace (team, feature, ticket…). |
curl https://api.switchgate.ai/v1/chat/completions \
-H "Authorization: Bearer sg-live-…" \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-sonnet-4-6",
"messages": [{ "role": "user", "content": "Bonjour!" }],
"sg_fallbacks": ["openai/gpt-5.2"],
"metadata": { "feature": "onboarding-bot" }
}'
from openai import OpenAI
client = OpenAI(
base_url="https://api.switchgate.ai/v1",
api_key="sg-live-…",
)
r = client.chat.completions.create(
model="anthropic/claude-sonnet-4-6",
messages=[{"role": "user", "content": "Bonjour!"}],
extra_body={
"sg_fallbacks": ["openai/gpt-5.2"],
"metadata": {"feature": "onboarding-bot"},
},
)
print(r.usage.model_extra["sg_cost_usd"])
{
"id": "cmpl-9f41c2",
"object": "chat.completion",
"created": 1786561200,
"model": "anthropic/claude-sonnet-4-6",
"provider": "anthropic",
"choices": [{
"index": 0,
"message": { "role": "assistant", "content": "Bonjour! Comment puis-je aider?" },
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 9,
"total_tokens": 21,
"sg_cost_usd": 0.000171,
"sg_cache": "miss"
}
}
Streaming: with "stream": true the endpoint emits OpenAI-style SSE chunks; the terminal chunk carries the usage object including sg_cost_usd, so exact cost is available even on streams.
GET/v1/models
List every model your key can call - the public catalog minus your organization's locks, plus your private models. GET/v1/models/{id} returns one entry.
{
"object": "list",
"data": [
{
"id": "anthropic/claude-sonnet-4-6",
"object": "model",
"provider": "anthropic",
"context_window": 200000,
"pricing": { "input_per_m_usd": 3.0, "output_per_m_usd": 15.0 },
"modalities": ["text"],
"locked_for_key": false
},
{
"id": "private/acme/legal-qwen-72b",
"object": "model",
"provider": "model-tunnel",
"pricing": { "input_per_m_usd": 0, "output_per_m_usd": 0 }
}
]
}
POST/v1/embeddings
OpenAI-compatible embeddings across providers. Embedding models are listed alongside chat models in GET /v1/models; they don't appear in the browse catalog, which shows chat and reasoning models only.
| Parameter | Type | Description |
|---|---|---|
modelrequired | string | An embedding model ID, e.g. openai/text-embedding-4. |
inputrequired | string | array | Text or array of texts to embed. |
dimensions | integer | Requested vector size, where the model supports it. |
POST/v1/keys
Create a key assigned to a user, with governance attached at birth. Requires an admin key. GET/v1/keys lists keys; GET/v1/keys/{id} reads one, POST/v1/keys/{id} updates mutable fields (budgets, locks, IPs, countries, PII policy, expiry), and POST/v1/keys/{id}/revoke kills one instantly.
| Parameter | Type | Description |
|---|---|---|
namerequired | string | Human label shown on the ledger. |
user_emailrequired | string | The named person (or service identity) this key belongs to. |
monthly_budget_usd | number | Spend cap enforced before purchase. Omit to inherit the user's budget. |
allowed_models | array | Model lock list; requests outside it return 403 model_locked. |
prepaid_usd | number | Makes the key burnable: fixed balance, stops at zero. |
expires_at | string | ISO 8601; the key self-revokes at this moment. |
allowed_ips | array | CIDR allowlist, e.g. ["203.0.113.0/24"]; requests from elsewhere return 403 ip_not_allowed. Pro+ |
allowed_countries | array | ISO 3166-1 alpha-2 list, e.g. ["CA","FR"], resolved at the edge. Team+ |
pii_redaction | string | off (default), mask (detected PII replaced with typed placeholders before leaving for the provider) or block (request refused with 422 pii_detected). Team+ |
sg_cache_default | string | Default cache mode for this key: auto, semantic or off. Hits always bill 0 credits. |
rate_limit_rpm | integer | Requests-per-minute cap for this key, enforced at the edge with 429 rate_limited. Spend caps alone aren't enough: a leaked key capped at $50 can still hammer you 10,000 times a minute - cap the rate too. |
max_concurrency | integer | Maximum simultaneous in-flight requests for this key; the excess queues briefly, then sheds with 429 rate_limited. |
curl https://api.switchgate.ai/v1/keys \
-H "Authorization: Bearer sg-admin-…" \
-d '{
"name": "workshop-badge-042",
"user_email": "[email protected]",
"prepaid_usd": 5,
"expires_at": "2026-09-01T00:00:00Z",
"allowed_models": ["deepseek/deepseek-v3.2", "openai/gpt-5-mini"]
}'
{
"id": "key_7g2h1",
"object": "api_key",
"name": "workshop-badge-042",
"key": "sg-live-…", // shown once, at creation only
"user_email": "[email protected]",
"prepaid_usd": 5,
"balance_usd": 5,
"allowed_models": ["deepseek/deepseek-v3.2", "openai/gpt-5-mini"],
"allowed_ips": [],
"allowed_countries": [],
"pii_redaction": "off",
"sg_cache_default": "auto",
"rate_limit_rpm": 60,
"max_concurrency": 8,
"expires_at": "2026-09-01T00:00:00Z",
"created_at": "2026-08-12T14:02:11Z",
"status": "active"
}
GET/v1/tunnels Enterprise
List your organization's Model Tunnels: the outbound connections your servers hold open to the gateway, and the models each one publishes. POST/v1/tunnels/{id}/rotate rotates a tunnel's mTLS identity; POST/v1/tunnels/{id}/revoke severs it immediately. Tunnels are created by the tunnel binary itself (switchgate tunnel up), never by the API - the credential is minted on your hardware.
{
"object": "list",
"data": [{
"id": "tun_a81xk",
"object": "tunnel",
"status": "connected",
"runtime": "vllm",
"replicas": 2,
"published_models": ["private/acme/legal-qwen-72b"],
"allowed_teams": ["platform", "legal-ai"],
"max_concurrency": 32,
"connected_since": "2026-08-01T09:12:44Z",
"last_heartbeat": "2026-08-12T14:03:02Z"
}],
"has_more": false
}
GET/v1/usage
The ledger, queryable. Sums always reconcile to billing to the cent. For raw per-call traces, GET/v1/usage/events returns individual requests (paginated) with model, tokens, cost, cache status, latency, key, user and your metadata.
| Parameter | Type | Description |
|---|---|---|
from / to | string | ISO 8601 window; defaults to the current month. |
group_by | string | user, key, model, provider or metadata.<field>. |
format | string | json (default) or csv for the finance team. |
{
"from": "2026-08-01T00:00:00Z",
"to": "2026-08-12T23:59:59Z",
"groups": [
{ "user": "[email protected]", "requests": 18422, "cost_usd": 31.07, "cache_hits": 6120 },
{ "user": "[email protected]", "requests": 9114, "cost_usd": 12.55, "cache_hits": 801 }
],
"total_cost_usd": 43.62
}
Errors
Errors use conventional status codes with a machine-readable error.code. Anything the gateway retried on your behalf is annotated in the trace.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | Malformed body or unsupported parameter for this model. |
| 401 | invalid_key | Missing, revoked or expired key. |
| 402 | insufficient_credits | Org balance or a prepaid key hit zero. |
| 403 | model_locked | The key's model locks exclude this model. |
| 403 | budget_exceeded | Per-user or per-key budget cap reached. |
| 403 | ip_not_allowed / country_not_allowed | The key's IP/CIDR or country restrictions rejected the caller. |
| 422 | pii_detected | The key's PII policy is block and personal data was found in the request. |
| 404 | model_not_found | Unknown model ID. |
| 429 | rate_limited | Provider or gateway rate limit; honor retry-after. |
| 429 | breaker_tripped | The runaway-agent kill switch paused this key; body includes the tripped rule. |
| 502 | provider_error | Upstream failure after retries and any sg_fallbacks were exhausted. |
| 504 | provider_timeout | Upstream exceeded the timeout after retries. |
Response headers & limits
| Header | Meaning |
|---|---|
x-request-id | Trace ID; quote it to support and find it on the ledger. |
x-sg-cost | Exact cost of this call in USD. 0 on cache hits and local models. |
x-sg-cache | hit, semantic-hit or miss. |
x-sg-provider | Provider that actually served the request (relevant with auto and fallbacks). |
x-sg-budget-remaining | The calling user's remaining budget for the period, in USD. |
x-ratelimit-limit / x-ratelimit-remaining / x-ratelimit-reset | Requests-per-minute window for this key: ceiling, remaining, and the epoch second the window resets. |
x-sg-pii | clean, masked (with the count of redacted spans) or absent when the key's policy is off. |
Gateway rate limits are generous and per-organization; provider limits pass through. Idempotency: send an idempotency-key header on writes (key creation) to make retries safe.
Every endpoint, one key away
Free trial credits; the models list is a good first call.