Skip to Content
SDKsSDKs

SDKs

Add governance to your AI requests in one change.

Keel evaluates requests before execution and records outcomes after. Requests are only executed if a permit is granted.

Install

pip install keel-sdk

Change one line

The Keel SDK keeps the provider-shaped calling pattern you already use. Change the import and keep the governance step in front of the provider call.

# Before from openai import OpenAI # After from keel_sdk.providers.openai import OpenAI

Provider-shaped request flow. Provider-shaped responses where the wrapper documents them. Governance runs before the provider call.

Working example

from keel_sdk.providers.openai import OpenAI client = OpenAI() response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], max_tokens=128, ) print(response["choices"][0]["message"]["content"])

Get your API key

Keel requires a project API key to evaluate and govern requests.

Sign up for Keel to get started. In the Keel dashboard , create a project, generate your API key, add a provider key, and set your first control.

Once approved, set your key:

export KEEL_BASE_URL="https://api.keelapi.com" export KEEL_API_KEY="keel_sk_your_project_key" export KEEL_PROJECT_ID="your-project-uuid"

You can also pass these directly when constructing the client:

from keel_sdk.providers.openai import OpenAI client = OpenAI( keel_base_url="https://api.keelapi.com", keel_api_key="keel_sk_...", keel_project_id="your-project-uuid", )

Provider API keys (OpenAI, Anthropic, etc.) are stored in your Keel project. The wrapper does not need them — Keel manages provider credentials on your behalf.

How it works

Every wrapper call runs the same governance flow:

Evaluate → Decide → Execute → Record

  1. Evaluate — Keel evaluates your project’s policies and budget against the request.
  2. Decide — If denied, the wrapper raises an error immediately. No provider call is made.
  3. Execute — If permitted, Keel forwards the request using your project’s stored provider key.
  4. Record — Usage, decision, and routing evidence are recorded for later readback.

Your code sees a normal provider response. A policy denial surfaces as an explicit error.

Provider wrappers

Each wrapper mirrors the native SDK interface for that provider. No new methods to learn.

OpenAI

from keel_sdk.providers.openai import OpenAI client = OpenAI() response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], max_tokens=128, ) print(response["choices"][0]["message"]["content"])

Anthropic

from keel_sdk.providers.anthropic import Anthropic client = Anthropic() response = client.messages.create( model="claude-sonnet-4-6", messages=[{"role": "user", "content": "Hello!"}], max_tokens=128, ) print(response["content"][0]["text"])

Google

from keel_sdk.providers.google import GenerativeModel model = GenerativeModel("gemini-2.5-flash") response = model.generate_content("Hello!")

xAI

from keel_sdk.providers.xai import Grok client = Grok() response = client.chat.completions.create( model="grok-3-mini", messages=[{"role": "user", "content": "Hello!"}], max_tokens=128, )

Meta

from keel_sdk.providers.meta import Llama client = Llama() response = client.chat.completions.create( model="llama-4-scout", messages=[{"role": "user", "content": "Hello!"}], max_tokens=128, )

Provider wrapper scope

All five provider wrappers expose synchronous text generation and streaming. For image, audio, embedding, and other modality operations, use the provider-neutral /v1/executions endpoint — see Executions for the supported operation × provider matrix.

Streaming

Streaming is supported for text generation across all five providers. Pass stream=True (Python) or stream: true (TypeScript) and iterate over chunks. Usage is automatically reported after the stream completes. Advanced operations should be verified for streaming support in your specific deployment.

from keel_sdk.providers.openai import OpenAI client = OpenAI() stream = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Write a haiku about governance."}], max_tokens=64, stream=True, ) for chunk in stream: delta = chunk.get("choices", [{}])[0].get("delta", {}) if "content" in delta: print(delta["content"], end="", flush=True)

Usage is automatically reported after the stream completes.

Error handling

When governance blocks a request, the wrapper stops before any provider call. In TypeScript and Python this surfaces as a KeelError. In Go, the core client also exposes typed *keel.KeelError and *keel.ThrottledError values for API and throttle handling.

from keel_sdk import KeelError from keel_sdk.providers.openai import OpenAI client = OpenAI() try: response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], max_tokens=128, ) except KeelError as e: print(f"Keel error {e.status}: [{e.code}] {e.message}")

Common cases to handle:

  • permit_denied on TypeScript and Python wrapper denials before any provider call.
  • budget.rate_limit_throttled on throttle paths, exposed via throttle-specific fields such as reasonCode or reason_code.
  • Upstream provider failures after an allow decision, surfaced as normal SDK errors with the provider or proxy message attached.

Advanced: Direct client access

For teams that need fine-grained control over individual governance steps, the KeelClient provides direct access to permits, executions, proxy, and other surfaces.

Supported SDKs

SDKBest forSurface
TypeScriptNode and TypeScript servicesTyped sub-clients for permits, executions (sync + stream), execute, proxy, jobs, API keys, and request timeline
PythonBackend services and scriptsSame surface as TypeScript with sync and async variants for every method
GoBackend servicesTyped clients for permits, executions (sync + stream), execute, proxy, jobs, API keys, and request timeline

Permit-only

Use this when your app wants decision-first governance and will call the provider itself.

import { KeelClient } from 'keel-sdk' const client = new KeelClient({ baseUrl: 'https://api.keelapi.com', apiKey: process.env.KEEL_API_KEY! }) const permit = await client.permits.create({ project_id: process.env.KEEL_PROJECT_ID!, idempotency_key: 'permit-only-001', subject: { type: 'user', id: 'usr_123' }, action: { name: 'ai.generate.summary' }, resource: { type: 'request', id: 'req_123', attributes: { provider: 'openai', model: 'gpt-4o-mini', operation: 'generate.text', estimated_input_tokens: 120, estimated_output_tokens: 80, max_output_tokens_requested: 120 } } }) if (permit.decision !== 'allow') { throw new Error(`blocked: ${permit.reason}`) } console.log(permit.id) console.log(permit.decision)

Permit creation requires project_id in the body.

Execution routes do not use this body field. Use the Idempotency-Key header instead.

Provider-neutral execution: /v1/executions

Use this when you want Keel to execute and your request is provider-neutral.

const response = await fetch('https://api.keelapi.com/v1/executions', { method: 'POST', headers: { Authorization: `Bearer ${process.env.KEEL_API_KEY!}`, 'Content-Type': 'application/json', 'Idempotency-Key': 'executions-001' }, body: JSON.stringify({ operation: 'generate.text', messages: [ { role: 'user', content: 'Reply in one short sentence.' } ], routing: { provider: 'openai', model: 'gpt-4o-mini' }, parameters: { max_output_tokens: 80 } }) }) const execution = await response.json() console.log(execution.id) console.log(execution.status) console.log(execution.governance?.permit_id ?? null) console.log(execution.output?.content?.[0]?.text ?? null)

Execution routes infer project context from the project API key and do not require project_id in the request body.

Provider-shaped execution: /v1/execute

Use this when you want Keel to execute and your request is provider-shaped.

const response = await fetch('https://api.keelapi.com/v1/execute', { method: 'POST', headers: { Authorization: `Bearer ${process.env.KEEL_API_KEY!}`, 'Content-Type': 'application/json', 'Idempotency-Key': 'execute-001' }, body: JSON.stringify({ provider: 'openai', model: 'gpt-4o-mini', input: { messages: [ { role: 'user', content: 'Reply with one sentence.' } ], max_tokens: 80 } }) }) const execution = await response.json() console.log(execution.id) console.log(execution.status) console.log(execution.resolved?.provider ?? null) console.log(execution.output?.content?.[0]?.text ?? null)

Execution routes infer project context from the project API key and do not require project_id in the request body.

Provider-native proxy: /v1/proxy/*

Use this when you need provider-native payloads and provider-native responses.

const response = await fetch('https://api.keelapi.com/v1/proxy/openai', { method: 'POST', headers: { Authorization: `Bearer ${process.env.KEEL_API_KEY!}`, 'Content-Type': 'application/json', 'Idempotency-Key': 'proxy-001' }, body: JSON.stringify({ model: 'gpt-4o-mini', messages: [ { role: 'user', content: 'Say hello.' } ], max_tokens: 32 }) }) const proxyData = await response.json() console.log(proxyData.id) console.log(proxyData.model) console.log(proxyData.choices?.[0]?.message?.content ?? null)

Proxy routes return provider-native responses, not the normalized execution envelope.

Common patterns

Parse normalized execution results

Use the same parser shape for /v1/executions and /v1/execute.

function readExecution(data: any) { return { requestId: data.id, status: data.status, permitId: data.governance?.permit_id ?? null, text: data.output?.content?.find((block: any) => block.type === 'output_text')?.text ?? null, errorCode: data.error?.code ?? null, errorMessage: data.error?.message ?? null } }

Permit-only gating

Check policy first, then skip the provider call if Keel says no.

import { KeelClient } from 'keel-sdk' const client = new KeelClient({ baseUrl: 'https://api.keelapi.com', apiKey: process.env.KEEL_API_KEY! }) const permit = await client.permits.create({ project_id: process.env.KEEL_PROJECT_ID!, idempotency_key: 'gate-001', subject: { type: 'user', id: 'usr_123' }, action: { name: 'ai.generate.summary' }, resource: { type: 'request', id: 'req_gate_001', attributes: { provider: 'openai', model: 'gpt-4o-mini', operation: 'generate.text', modality: 'text', execution_mode: 'sync', estimated_input_tokens: 120, estimated_output_tokens: 80, max_output_tokens_requested: 120 } } }) if (permit.decision !== 'allow') { console.log('blocked:', permit.reason) } else { console.log('safe to call provider') }

Handle normalized envelope errors vs top-level API errors

function readKeelFailure(data: any) { if (data?.object === 'execution') { return { kind: 'execution-envelope', status: data.status, code: data.error?.code ?? null, message: data.error?.message ?? null } } return { kind: 'top-level-api-error', status: 'error', code: data?.error?.code ?? null, message: data?.error?.message ?? null } }

Provider-native proxy parsing

Do not reuse normalized-envelope parsing on proxy routes.

function readOpenAIProxy(data: any) { return { requestId: data.id, text: data.choices?.[0]?.message?.content ?? null, finishReason: data.choices?.[0]?.finish_reason ?? null, providerErrorMessage: data.error?.message ?? null } }

Auth

Project API key

Create a project API key in the Keel dashboard for the project that will call the SDK. Send it as Authorization: Bearer <project_api_key>.

Provider keys are handled by Keel

For /v1/execute, /v1/executions, /v1/proxy/*, and /v1/jobs, Keel uses the provider key stored on that project. Your app does not send raw OpenAI, Anthropic, Google, xAI, or Meta keys on these routes.

Where to get keys

  • Create the runtime project API key in the Keel dashboard for your project.
  • Add or rotate provider keys in the same project before the first governed execution.
  • If you use permit-only mode and call the provider yourself, your app still needs its own provider credential for that second step.

Where to go next

  • Permits — decision-first governance
  • Executions — provider-neutral execution
  • Execute — provider-shaped execution
  • Proxy Execution — provider-native passthrough
  • Errors — error codes and retry guidance

Managing Keel at scale

Terraform configures Keel resources through infrastructure as code. It is not used to execute or proxy AI requests.

→ Configure with Terraform

Last updated on Edit this page on GitHubÂ