Unified API
Base URL https://api.swurl.ai/v1. The request and response shapes follow the OpenAI chat completions schema, so most existing clients work by changing two settings.
Authentication
Bearer token in the Authorization header. Keys are workspace-scoped and carry the permissions of the seat that created them.
Authorization: Bearer swurl_sk_live_...
Chat completions
POST /v1/chat/completions
| Field | Type | Notes |
|---|---|---|
model | string | A model id from /v1/models, or auto to let Auto-Route choose, or a tier such as auto:fast. |
messages | array | Standard role/content pairs. system, user, assistant, tool. |
stream | boolean | Server-sent events when true. |
max_tokens | integer | Clamped to the target model's ceiling rather than erroring. |
temperature | number | Normalised across providers to a 0–2 scale. |
tools | array | OpenAI tool-call schema. Translated per provider; unsupported models are excluded from routing when present. |
swurl | object | Swurl-specific options. See below. |
The swurl options object
{
"swurl": {
"route": "balanced", // conservative | balanced | aggressive
"exclude": ["grok-4"], // never route here
"require_zero_retention": true,
"region": "eu", // us | eu | au (Enterprise)
"budget_cents": 25, // refuse if this call would exceed
"trace": true // include the routing rationale
}
}
Streaming
Set "stream": true and read server-sent events. Chunks follow the OpenAI delta format. The final event before [DONE] carries the usage and routing summary.
data: {"choices":[{"delta":{"content":"Three"}}]}
data: {"choices":[{"delta":{"content":" conflicts"}}]}
data: {"swurl":{"model":"claude-opus-4.5","cost_cents":6.2,"tokens":{"in":18402,"out":611}}}
data: [DONE]
Routing metadata
Every non-streaming response carries a swurl object alongside the standard fields:
"swurl": {
"model": "gemini-3-flash",
"provider": "google",
"routed": true,
"task_class": "extraction",
"reason": "cheapest model above quality threshold for extraction",
"considered": ["gemini-3-flash", "haiku-4.5", "llama-4-scout"],
"cost_cents": 0.14,
"latency_ms": 412,
"tokens": { "in": 1204, "out": 96 }
}
Set trace to false to omit considered and reason and shave a little response size.
List models
GET /v1/models returns everything your workspace can reach, filtered by plan, region pin and any exclusions set at workspace level.
{
"data": [
{
"id": "claude-opus-4.5",
"provider": "anthropic",
"tier": "frontier",
"context_window": 500000,
"rate_per_1m_usd": 11.25,
"supports": ["tools", "vision", "zero_retention"]
}
]
}
Usage
GET /v1/usage?from=2026-08-01&to=2026-08-23&group_by=model returns metered spend for the period. group_by accepts model, provider, member, day or task_class. This is the same data the analytics view renders.
Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | Malformed body, or a parameter the target model cannot accept. |
| 401 | invalid_key | Missing, revoked or malformed key. |
| 402 | credit_exhausted | Pool is empty and the workspace has the hard stop enabled. |
| 403 | model_not_available | Plan, region pin or workspace exclusion blocks this model. |
| 404 | unknown_model | No such model id. |
| 429 | rate_limited | Back off; honour Retry-After. |
| 502 | provider_error | Upstream failed and no fallback was eligible. Safe to retry. |
| 503 | no_route | Every candidate model is excluded or degraded. Widen the constraints. |
Errors return a stable code; branch on that rather than on the message text, which may be reworded.

