Skip to Content
API ReferencePython SDK Reference

Python SDK Reference

The curate-me Python package provides two main interfaces:

  1. CurateGateway — configures OpenAI and Anthropic SDK clients to route through the gateway
  2. GatewayAdmin — manages policies, secrets, API keys, and queries usage

Installation

pip install curate-me

Optional dependencies for provider SDKs:

pip install curate-me openai # For OpenAI integration pip install curate-me anthropic # For Anthropic integration pip install curate-me openai anthropic # For both

The SDK uses httpx for HTTP requests (included as a dependency).

CurateGateway

The gateway client wraps provider SDKs so they route through the Curate-Me gateway.

Constructor

from curate_me.gateway import CurateGateway gw = CurateGateway( api_key="cm_sk_your_key", gateway_url="https://api.curate-me.ai", # default timeout=60.0, # seconds, default retry=RetryPolicy(max_retries=2), # optional )
ParameterTypeDefaultDescription
api_keystrrequiredYour Curate-Me API key (cm_sk_...)
gateway_urlstrhttps://api.curate-me.aiBase URL of the gateway
timeoutfloat60.0Request timeout in seconds
retryRetryPolicy2 retries, exponential backoffRetry policy

openai()

Returns an openai.OpenAI client configured to proxy through the gateway.

client = gw.openai(provider_key="sk-your-openai-key") # Or use a stored secret (no provider key needed) client = gw.openai() response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], ) print(response.choices[0].message.content)

async_openai()

Returns an openai.AsyncOpenAI client for async usage.

client = gw.async_openai(provider_key="sk-your-openai-key") response = await client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Hello!"}], )

anthropic()

Returns an anthropic.Anthropic client configured to proxy through the gateway.

client = gw.anthropic(provider_key="sk-ant-your-key") # Or use a stored secret client = gw.anthropic() message = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=1024, messages=[{"role": "user", "content": "Hello, Claude!"}], ) print(message.content[0].text)

async_anthropic()

Returns an anthropic.AsyncAnthropic client for async usage.

client = gw.async_anthropic(provider_key="sk-ant-your-key") message = await client.messages.create( model="claude-sonnet-4-20250514", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}], )

openai_config() / anthropic_config()

Returns a config dict you can spread into the SDK constructor for custom configuration:

config = gw.openai_config(provider_key="sk-your-key") # config = { # "api_key": "sk-your-key", # "base_url": "https://api.curate-me.ai/v1", # "default_headers": {"X-CM-API-Key": "cm_sk_your_key", "X-Provider-Key": "sk-your-key"}, # "timeout": 60.0, # "max_retries": 2, # } from openai import OpenAI client = OpenAI(**config)

admin()

Returns a GatewayAdmin client for managing the gateway.

admin = gw.admin()

GatewayAdmin

The admin client provides async methods for managing gateway resources.

Constructor

from curate_me.gateway import GatewayAdmin admin = GatewayAdmin( api_key="cm_sk_your_key", gateway_url="https://api.curate-me.ai", timeout=30.0, retry=RetryPolicy(max_retries=2), )

Or create it from a CurateGateway instance:

admin = gw.admin()

Policies

get_policies()

policies = await admin.get_policies() # policies = {"org_id": "org_abc123", "rpm_limit": 60, "daily_budget": 25.0, ...}

get_policy(org_id)

policy = await admin.get_policy("org_abc123")

set_policy(org_id, policy)

Replace the governance policy for an org (PUT semantics):

await admin.set_policy("org_abc123", { "rpm_limit": 100, "daily_budget": 50.0, "monthly_budget": 1000.0, "max_cost_per_request": 2.0, "pii_scan_enabled": True, "pii_action": "block", "allowed_models": ["gpt-4o-mini", "claude-haiku-3-5-20241022"], "hitl_cost_threshold": 10.0, })

update_policy(policy)

Create or update a policy (POST semantics):

await admin.update_policy({ "org_id": "org_abc123", "daily_budget": 100.0, })

delete_policy(org_id)

Delete a policy, reverting to tier defaults:

await admin.delete_policy("org_abc123")

simulate_policy(draft_policy, replay_hours)

Simulate a draft policy against recent traffic:

result = await admin.simulate_policy( draft_policy={"rpm_limit": 30, "daily_budget": 10.0}, replay_hours=24, ) # Shows how many requests would have been blocked

Usage and Costs

get_usage(days, limit, org_id)

usage = await admin.get_usage(days=7, limit=100) # usage = {"records": [...], "total": 847}

get_daily_costs(days, org_id)

costs = await admin.get_daily_costs(days=30) # costs = {"days": [{"date": "2026-03-17", "total_cost": 42.15, ...}, ...]}

get_usage_record(request_id)

record = await admin.get_usage_record("gw_a1b2c3d4") # record = {"request_id": "gw_a1b2c3d4", "model": "gpt-4o", "actual_cost": 0.0031, ...}

Latency

get_latency_stats()

stats = await admin.get_latency_stats() # stats = {"providers": {"openai": {"p50_ms": 320, "p95_ms": 890, "p99_ms": 1450}}}

API Keys

list_api_keys()

keys = await admin.list_api_keys() # keys = {"keys": [{"key_id": "key_xyz", "name": "prod", "created_at": "..."}], "total": 3}

create_api_key(name, scopes)

new_key = await admin.create_api_key( name="staging-api", scopes=["proxy"], ) # new_key = {"key_id": "key_new123", "key": "cm_sk_abc...", "name": "staging-api"} # The plaintext "key" field is shown only once

Secrets (stored provider keys)

store_secret(provider, key, label)

await admin.store_secret( provider="openai", key="sk-your-openai-key", label="Production OpenAI", )

list_secrets(org_id)

secrets = await admin.list_secrets() # secrets = {"secrets": [{"provider": "openai", "label": "Production OpenAI", ...}]}

rotate_secret(provider, new_key)

await admin.rotate_secret("openai", "sk-new-openai-key")

revoke_secret(provider)

await admin.revoke_secret("openai")

Models

list_models()

List available models from all configured providers:

models = await admin.list_models() # models = {"object": "list", "data": [{"id": "gpt-4o", "object": "model", ...}, ...]}

Provider Targets

get_provider_targets(org_id)

targets = await admin.get_provider_targets()

trigger_discovery(provider_id, api_key)

result = await admin.trigger_discovery("openai") # result = {"provider_id": "openai", "models": ["gpt-4o", "gpt-4o-mini", ...], "total": 12}

Discovery

get_discovery_catalog(provider_id, limit, cursor, org_id)

catalog = await admin.get_discovery_catalog(limit=50)

get_discovery_history(provider_id, limit, cursor, org_id)

history = await admin.get_discovery_history(limit=10)

HITL (Human-in-the-Loop)

wait_for_approval(approval_id, timeout)

Poll an approval until it is resolved or times out:

result = await admin.wait_for_approval("apr_abc123", timeout=300) # result = {"approval_id": "apr_abc123", "status": "approved", ...}

Raises TimeoutError if not resolved within the timeout.

Policy GitOps

validate_policy_bundle(bundle, strict)

result = await admin.validate_policy_bundle(bundle=my_policy_bundle, strict=True)

export_policy_bundle(org_id, version)

bundle = await admin.export_policy_bundle("org_abc123")

import_policy_bundle(org_id, bundle, dry_run, merge_strategy)

result = await admin.import_policy_bundle( "org_abc123", bundle=my_policy_bundle, dry_run=True, merge_strategy="replace", )

diff_policy(org_id, draft_policy)

diff = await admin.diff_policy("org_abc123", draft_policy={"rpm_limit": 30})

list_policy_versions(org_id, limit)

versions = await admin.list_policy_versions("org_abc123", limit=10)

rollback_policy(org_id, target_version, reason)

await admin.rollback_policy("org_abc123", target_version="v3", reason="Reverted budget change")

RetryPolicy

Configure retry behavior for transient failures:

from curate_me.gateway import RetryPolicy policy = RetryPolicy( max_retries=3, # default: 2 initial_delay=1.0, # seconds, default: 0.5 max_delay=16.0, # seconds, default: 8.0 backoff_factor=2.0, # default: 2.0 retryable_status_codes={429, 500, 502, 503, 504}, # defaults )

Error handling

The SDK raises typed exceptions for gateway errors:

from curate_me.gateway import ( GatewayError, GatewayAuthenticationError, GatewayGovernanceError, GatewayRateLimitError, ) try: usage = await admin.get_usage() except GatewayAuthenticationError as e: print(f"Bad API key: {e}") print(f"Request ID: {e.request_id}") except GatewayGovernanceError as e: print(f"Blocked by policy: {e}") except GatewayRateLimitError as e: print(f"Rate limited. Retry after: {e.retry_after}s") except GatewayError as e: print(f"Gateway error [{e.status_code}]: {e}")
ExceptionStatusWhen
GatewayAuthenticationError401Invalid or missing API key
GatewayGovernanceError403Blocked by governance policy
GatewayRateLimitError429Rate limit exceeded
GatewayErroranyAll other gateway errors

Streaming error recovery

The SDK includes resilient streaming helpers that automatically retry on mid-stream failures:

from curate_me import resilient_openai_stream, StreamRetryConfig client = gw.openai(provider_key="sk-your-key") async for event in resilient_openai_stream( client.chat.completions.create, model="gpt-4o", messages=[{"role": "user", "content": "Tell me a long story"}], stream=True, retry_config=StreamRetryConfig(max_retries=3), ): if event.type == "content": print(event.content, end="") elif event.type == "error": print(f"\nStream error (retrying): {event.error}")

Complete example

import asyncio from curate_me.gateway import CurateGateway async def main(): gw = CurateGateway(api_key="cm_sk_your_key") # 1. Store your OpenAI key (one-time setup) admin = gw.admin() await admin.store_secret("openai", "sk-your-openai-key", label="Production") # 2. Set governance policy await admin.set_policy("org_abc123", { "rpm_limit": 100, "daily_budget": 50.0, "max_cost_per_request": 2.0, "pii_scan_enabled": True, "allowed_models": ["gpt-4o", "gpt-4o-mini"], "hitl_cost_threshold": 10.0, }) # 3. Make governed requests (no provider key needed -- stored secret) client = gw.openai() response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Summarize this document..."}], ) print(response.choices[0].message.content) # 4. Check costs costs = await admin.get_daily_costs(days=7) for day in costs.get("days", []): print(f"{day['date']}: ${day['total_cost']:.2f}") asyncio.run(main())

Next steps