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

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.

Authorization header
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.

ParameterTypeDescription
modelrequiredstringCatalog ID like anthropic/claude-sonnet-4-6, switchgate/auto, or local/<model>.
messagesrequiredarrayChat messages, OpenAI format: [{"role":"user","content":"…"}]. Multimodal content parts supported where the model supports them.
streambooleanServer-sent events when true. Final chunk includes usage and exact cost.
temperaturenumber0 - 2, passed through to the provider. Provider defaults apply when omitted.
max_tokensintegerOutput cap, passed through.
toolsarrayTool definitions, OpenAI format; forwarded to models with tool support.
tool_choicestring | objectauto, none, required, or a specific tool.
response_formatobject{"type":"json_object"} or {"type":"json_schema","json_schema":{…}} where the model supports structured output.
top_pnumberNucleus sampling, passed through.
stopstring | arrayUp to 4 stop sequences.
frequency_penalty / presence_penaltynumber-2 to 2, passed through where supported.
seedintegerBest-effort determinism on providers that honor it.
userstringYour end-user identifier; appears on the ledger for per-customer accounting.
sg_pii_redactionstringPer-request override of the key's PII policy: may only be stricter (mask → block), never looser.
sg_cachestring"auto" (default, exact-match), "semantic" (paraphrase matching, key must opt in) or "off". Hits bill 0 credits.
sg_fallbacksarrayOrdered model IDs to fail over to on provider error or timeout, e.g. ["openai/gpt-5.2","google/gemini-3-pro"].
metadataobjectUp to 8 string key-values attached to the ledger trace (team, feature, ticket…).
Request - cURL
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" }
  }'
Request - Python
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"])
Response - 200
{
  "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.

Response - 200 (truncated)
{
  "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.

ParameterTypeDescription
modelrequiredstringAn embedding model ID, e.g. openai/text-embedding-4.
inputrequiredstring | arrayText or array of texts to embed.
dimensionsintegerRequested 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.

ParameterTypeDescription
namerequiredstringHuman label shown on the ledger.
user_emailrequiredstringThe named person (or service identity) this key belongs to.
monthly_budget_usdnumberSpend cap enforced before purchase. Omit to inherit the user's budget.
allowed_modelsarrayModel lock list; requests outside it return 403 model_locked.
prepaid_usdnumberMakes the key burnable: fixed balance, stops at zero.
expires_atstringISO 8601; the key self-revokes at this moment.
allowed_ipsarrayCIDR allowlist, e.g. ["203.0.113.0/24"]; requests from elsewhere return 403 ip_not_allowed. Pro+
allowed_countriesarrayISO 3166-1 alpha-2 list, e.g. ["CA","FR"], resolved at the edge. Team+
pii_redactionstringoff (default), mask (detected PII replaced with typed placeholders before leaving for the provider) or block (request refused with 422 pii_detected). Team+
sg_cache_defaultstringDefault cache mode for this key: auto, semantic or off. Hits always bill 0 credits.
rate_limit_rpmintegerRequests-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_concurrencyintegerMaximum simultaneous in-flight requests for this key; the excess queues briefly, then sheds with 429 rate_limited.
Request - cURL
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"]
  }'
Response - 201
{
  "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.

Response - 200
{
  "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.

ParameterTypeDescription
from / tostringISO 8601 window; defaults to the current month.
group_bystringuser, key, model, provider or metadata.<field>.
formatstringjson (default) or csv for the finance team.
Response - 200 (grouped by user)
{
  "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.

StatusCodeMeaning
400invalid_requestMalformed body or unsupported parameter for this model.
401invalid_keyMissing, revoked or expired key.
402insufficient_creditsOrg balance or a prepaid key hit zero.
403model_lockedThe key's model locks exclude this model.
403budget_exceededPer-user or per-key budget cap reached.
403ip_not_allowed / country_not_allowedThe key's IP/CIDR or country restrictions rejected the caller.
422pii_detectedThe key's PII policy is block and personal data was found in the request.
404model_not_foundUnknown model ID.
429rate_limitedProvider or gateway rate limit; honor retry-after.
429breaker_trippedThe runaway-agent kill switch paused this key; body includes the tripped rule.
502provider_errorUpstream failure after retries and any sg_fallbacks were exhausted.
504provider_timeoutUpstream exceeded the timeout after retries.

Response headers & limits

HeaderMeaning
x-request-idTrace ID; quote it to support and find it on the ledger.
x-sg-costExact cost of this call in USD. 0 on cache hits and local models.
x-sg-cachehit, semantic-hit or miss.
x-sg-providerProvider that actually served the request (relevant with auto and fallbacks).
x-sg-budget-remainingThe calling user's remaining budget for the period, in USD.
x-ratelimit-limit / x-ratelimit-remaining / x-ratelimit-resetRequests-per-minute window for this key: ceiling, remaining, and the epoch second the window resets.
x-sg-piiclean, 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.