Proxy Execution
Provider-native execution routes for Keel-managed requests.
For most use cases, use the SDK provider wrappers instead of calling proxy endpoints directly. The wrappers are provider-shaped clients for model calls through Keel: they handle permits, usage reporting, and error handling, and they refuse requests that carry tool definitions. They need a provider key stored in Keel and a policy that allows the model.
Use these routes when Keel should execute the provider request and record usage in the same path, and you need direct control over the proxy layer.
If provider-native payloads are not required, compare the execution surfaces in Quickstart.
Official public proxy routes
| Route | Streaming | Current exposed behavior |
|---|---|---|
POST /v1/proxy/openai | Yes | Text generation and streaming. Keel infers image understanding, GPT Image generation, audio transcription and audio generation from the payload shape, but currently refuses those requests before any provider call. Image edits are not supported. |
POST /v1/proxy/anthropic | Yes | Text messages with sync and streaming support; existing nonstreaming callers receive Keel’s OpenAI-shaped response envelope. |
POST /v1/proxy/anthropic/v1/messages | Yes | Official Anthropic SDK Messages path for native text requests and responses. Tool-schema calls and token counting are outside the documented SDK recipe. |
POST /v1/proxy/google | Yes | Text generation with sync and streaming support. Image-understanding requests are currently refused before any provider call. |
POST /v1/proxy/xai | Yes | Text and chat with sync and streaming support. |
POST /v1/proxy/meta | Yes | Text generation with sync and streaming support, plus an embeddings subset. Image-understanding requests are currently refused before any provider call. |
The five provider proxy families support SSE streaming for text generation; streaming has a current live certification on the OpenAI family only. Pass stream: true in the request payload. Requests that carry tool definitions are refused before dispatch (platform_safety_floor.unknown_action). Embeddings are sync-only. Keel dispatches only operations it can price authoritatively; image and audio operations are currently refused before dispatch (for example unsupported_operation, pricing_not_configured, or dispatch_model_not_authorized for GPT Image generation).
These routes remain provider-specific. Keel still does not expose a provider-neutral request shape here. Use POST /v1/executions when you want one canonical execution contract instead of route-specific proxy request and response shapes.
Proxy routes infer project context from the project API key; the provider-native body should not include project_id.
What the proxy path does
- authenticates the caller with a client-scoped project API key
- applies Keel governance before provider dispatch
- returns the documented shape for the selected route; the Anthropic SDK Messages path returns a native Message, while the original Anthropic nonstreaming route retains its existing OpenAI-shaped envelope
- enforces the route-specific execution controls that Keel documents publicly
- records governance, routing, usage, and request state for later readback
Idempotency on proxy routes
- Keel uses the
Idempotency-Keyheader for replay protection on proxy routes - same key plus the same payload can replay a complete stored response without another provider dispatch
- streaming replay requires the complete original SSE bytes within the bounded response store; if those bytes are unavailable, Keel refuses the retry with
409rather than returning an empty successful stream or dispatching again - historical normalized-only stream caches cannot reconstruct the original SSE and are refused with
409. Do not switch to a new idempotency key to force a redispatch; reconcile the original outcome first. - same key plus a different payload returns
409 conflict - if the header is omitted, Keel still executes the request, but retries are treated as new executions
Hosted proxy requests require the request-freshness headers documented in Request Freshness, including retries.
Prompt Firewall scope on proxy routes
Prompt Firewall currently runs on the public proxy routes for the prompt-bearing content Keel can extract from those payloads, including:
POST /v1/proxy/openaiPOST /v1/proxy/anthropicPOST /v1/proxy/googlePOST /v1/proxy/xaiPOST /v1/proxy/meta
That is still a route-dependent inspection boundary, not a claim of full provider parity across every payload field.
API example
curl
curl -i -sS https://api.keelapi.com/v1/proxy/openai \
-H "Authorization: Bearer keel_sk_your_key_here" \
-H "Content-Type: application/json" \
-H "X-Keel-Timestamp: $(date +%s)" \
-H "X-Keel-Nonce: $(openssl rand -hex 16)" \
-H "Idempotency-Key: proxy-001" \
-d '{
"model": "gpt-4o-mini",
"messages": [
{"role": "user", "content": "Summarize this text in one sentence."}
],
"max_tokens": 200
}'Proxy responses follow the route-specific shapes in the table above, not the normalized execution envelope. The original Anthropic route returns an OpenAI-shaped nonstreaming response; use its
/v1/messagesSDK path for a native Anthropic Message. Generate a fresh timestamp and nonce for every request. Hosted Keel requires both headers; see Request Freshness.Need the route matrix? See Route comparison.
Test this request in the Playground: Try in Playground
JavaScript
const response = await fetch('https://api.keelapi.com/v1/proxy/openai', {
method: 'POST',
headers: {
Authorization: 'Bearer keel_sk_your_key_here',
'Content-Type': 'application/json',
'X-Keel-Timestamp': Math.floor(Date.now() / 1000).toString(),
'X-Keel-Nonce': crypto.randomUUID(),
'Idempotency-Key': 'proxy-001'
},
body: JSON.stringify({
model: 'gpt-4o-mini',
messages: [
{ role: 'user', content: 'Summarize this text in one sentence.' }
],
max_tokens: 200
})
})
const data = await response.json()
console.log(data)Python
import requests
import secrets
import time
response = requests.post(
"https://api.keelapi.com/v1/proxy/openai",
headers={
"Authorization": "Bearer keel_sk_your_key_here",
"Content-Type": "application/json",
"X-Keel-Timestamp": str(int(time.time())),
"X-Keel-Nonce": secrets.token_hex(16),
"Idempotency-Key": "proxy-001",
},
json={
"model": "gpt-4o-mini",
"messages": [
{
"role": "user",
"content": "Summarize this text in one sentence.",
}
],
"max_tokens": 200,
},
)
print(response.json())Response example
{
"id": "chatcmpl-B9MBs8CjcvOU2jLn4n570S5qMJKcT",
"object": "chat.completion",
"created": 1764104521,
"model": "gpt-4o-mini-2024-07-18",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "The text says the team shipped the feature ahead of schedule and will monitor adoption over the next week."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 21,
"completion_tokens": 18,
"total_tokens": 39
}
}const requestId = data.id
const model = data.model
const text = data.choices?.[0]?.message?.content ?? null
const finishReason = data.choices?.[0]?.finish_reason ?? null
const providerErrorMessage = data.error?.message ?? nullRelated route guidance
For the canonical route matrix, see Route comparison.
Routing boundary
Routing and fallback controls are route-specific. Not all proxy routes support the same routing and fallback capabilities. See the provider support table for current coverage.
Current non-claims
- no claim that proxy routes are provider-neutral
- realtime governance uses
/v1/sessions; proxy routes do not carry realtime provider media or replace the customer-operated voice sidecar - advanced operation coverage (image generation, audio, embeddings) varies by provider — see the provider support table
- cost estimation accuracy varies by operation type, with multimodal operations relying on heuristic pre-dispatch estimates