Python SDK.
deeprelay_sdk is the official typed Python client. It is generated from the same OpenAPI spec the public REST API is served against, so every endpoint has a typed method and responses deserialize into pydantic models that validate as they parse. Python 3.10+.
It covers the serverless relay: inference (chat, embeddings, image and video generation), the model catalog, the subscription and billing lifecycle, usage reporting, webhooks, and long-running operations. Six API classes — InferenceApi, BillingApi, UsageApi, WebhooksApi, OperationsApi, MetaApi — and that is the whole client. (Python 3.9 has been end-of-life since 2025-10-31; the package metadata declares the 3.10 floor.)
§ 08.1Install¶
The package is published to PyPI as deeprelay-sdk and imports as deeprelay_sdk.
pip install deeprelay-sdkOn systems where pip refuses to install into the system Python (PEP 668, "externally managed environment", as on current macOS and Debian/Ubuntu), install inside a virtual environment first: python3 -m venv .venv then source .venv/bin/activate.
§ 08.2Base URL and authentication¶
| Base URL | https://api.deeprelay.ai/v1 |
| Auth | Authorization: Bearer deeprelay_live_… |
Get a key with deeprelay login (it stores a deeprelay_live_… key in ~/.config/deeprelay/credentials.json) or mint one in the dashboard. Pass it to the client as access_token on Configuration and the SDK sets the Authorization header for you.
Only /v1/health and /v1/openapi.json are reachable without a key. Every other endpoint — including the model catalog — is account-scoped, so the quick start below reads the key from the environment rather than hard-coding one.
§ 08.3Quick start¶
Nothing billable: a keyless health check, then the model catalog.
import os
import deeprelay_sdk
HOST = "https://api.deeprelay.ai/v1"
# /health takes no key.
with deeprelay_sdk.ApiClient(deeprelay_sdk.Configuration(host=HOST)) as anon:
print(deeprelay_sdk.MetaApi(anon).get_health().status)
api_key = os.environ["DEEPRELAY_API_KEY"] # e.g. from `deeprelay login`
cfg = deeprelay_sdk.Configuration(host=HOST, access_token=api_key)
with deeprelay_sdk.ApiClient(cfg) as client:
models = deeprelay_sdk.InferenceApi(client).list_models()
for m in models.data[:5]:
plan = "plan" if m.plan_covered else "pay-as-you-go"
print(f"{m.id:<44} {m.modality:<10} {plan}")export DEEPRELAY_API_KEY=deeprelay_live_...
python quickstart.pylist_models returns the OpenAI-shaped {object, data} envelope, not a cursor page. The genuinely paginated endpoints — list_usage, list_deposits, list_webhook_endpoints — return data plus a next_cursor that is None on the last page; pass it back as cursor= to walk forward.
§ 08.4Your first completion¶
This one does spend money: on the plan's quota if the model is covered, and pay-as-you-go otherwise.
from deeprelay_sdk.models.chat_completion_request import ChatCompletionRequest
from deeprelay_sdk.models.chat_message import ChatMessage
with deeprelay_sdk.ApiClient(cfg) as client:
resp = deeprelay_sdk.InferenceApi(client).create_chat_completion(
ChatCompletionRequest(
model="deeprelay/llama-3.3-70b-instruct",
messages=[ChatMessage(role="user", content="Say hello in six words.")],
max_tokens=64,
)
)
print(resp.choices[0].message.content)
print(f"tokens: {resp.usage.total_tokens}")§ 08.5Subscription and billing¶
The flat tier covers a published list of models; anything outside it, and anything past the quota, bills pay-as-you-go at list rates. Four calls cover the whole lifecycle.
What does my org have? Read-only, any member of the org:
with deeprelay_sdk.ApiClient(cfg) as client:
sub = deeprelay_sdk.BillingApi(client).get_subscription()
print(f"subscribed={sub.subscribed} status={sub.status}")
print(f"plan: {sub.plan.name}")
# Absent until the billing provider has reported a period — so None for an
# org that has never subscribed. Guard before formatting.
if sub.current_period_end:
print(f" period ends {sub.current_period_end:%Y-%m-%d}")
if sub.cancel_at_period_end:
print(" cancels at period end — coverage continues until then")
if sub.usage: # None when the org is not subscribed
q, u = sub.quota, sub.usage
print(f" input {u.weighted_input_tokens:,} / {q.input_tokens_monthly:,}")
print(f" output {u.weighted_output_tokens:,} / {q.output_tokens_monthly:,}")
print(f" weekly {u.weekly_weighted_tokens:,} / {q.weekly_tokens:,}")quota.payg_discount_bp is deprecated and always 0. There is no subscriber discount on pay-as-you-go spend; do not display it.
Will this call be covered? inference_preflight answers "what happens if I call this model right now?" against the same gates that judge the real request, without sending, counting, charging, or reserving anything:
with deeprelay_sdk.ApiClient(cfg) as client:
pre = deeprelay_sdk.InferenceApi(client).inference_preflight(
model="deeprelay/llama-3.3-70b-instruct"
)
print(f"{pre.verdict}: {pre.message}")
print(f" plan_covered={pre.plan_covered} subscribed={pre.subscribed} funded={pre.funded}")The case it exists for is verdict="warn" with reason="not_plan_covered": a subscriber calling a model outside the plan is served and is charged pay-as-you-go, and nothing in the completion response says so. funded is a boolean and never a figure — this route is on the inference read scope and must not disclose the balance. Use get_balance for the number.
Start a subscription. Requires billing:write and org-admin, because subscribing spends organization money:
with deeprelay_sdk.ApiClient(cfg) as client:
session = deeprelay_sdk.BillingApi(client).create_subscription_checkout()
print(f"Open this to subscribe: {session.checkout_url}")This does not subscribe anyone. Checkout is a hosted page that needs a browser and a card, so the caller's job is to put the returned URL in front of a human. The subscription becomes active when payment completes, which is not synchronous with this call — poll get_subscription to confirm. An empty request body means "use every default"; pass a SubscriptionCheckoutRequest to override success_url / cancel_url, or to pin plan_key to the plan you displayed so an unknown key 400s rather than silently buying a different tier.
Cancel, or fix a declined card. Both live in the hosted billing portal:
with deeprelay_sdk.ApiClient(cfg) as client:
portal = deeprelay_sdk.BillingApi(client).create_subscription_portal()
print(f"Manage billing here: {portal.portal_url}")Cancelling in the portal ends the subscription at the close of the current period; coverage continues until then and get_subscription reports cancel_at_period_end=True. An organization that has never paid for anything gets a 404 — there is no billing account to manage, and this endpoint deliberately does not create one as a side effect of looking.
The same client reaches get_balance, get_spending_limit / update_spending_limit, and the stablecoin deposit methods. Method names mirror the spec's operationIds in snake_case (createSubscriptionCheckout → create_subscription_checkout), and every method is documented in the generated tree.
§ 08.6Errors¶
Failed calls raise deeprelay_sdk.ApiException (or a status-specific subclass from deeprelay_sdk.exceptions such as NotFoundException / UnauthorizedException) carrying status, reason, and the response body:
try:
# an org with no billing account has no portal to open
deeprelay_sdk.BillingApi(client).create_subscription_portal()
except deeprelay_sdk.ApiException as e:
print(f"api error {e.status}: {e.body}")api error 404: {"type":"https://api.deeprelay.ai/errors/not_found","title":"Not Found",
"status":404,"detail":"no billing account for this organization","code":"not_found","request_id":"…"}The body is an RFC 9457 problem document; request_id is what to quote in a support email. Inference endpoints are the exception — they return OpenAI's error envelope instead, so that OpenAI-compatible clients parse failures the way they already do.
§ 08.7Versioning: pre-1.0 convention¶
The SDK is versioned independently of the API (the API is v1 and stays v1). While the SDK is on 0.x, it follows this convention (the numbers below are illustrative, not the current version; check the release tags for that):
- Breaking change → minor bump (
0.2.3→0.3.0) - Everything else (new endpoints, new fields, docstring changes) → patch bump (
0.2.3→0.2.4)
This is the usual pre-1.0 reading of semver, and it is what npm's caret operator already enforces for the TypeScript package. pip does not enforce anything comparable: pip install deeprelay-sdk takes the newest release, breaking changes included. If you need stability before 1.0, pin explicitly in your requirements (deeprelay_sdk==<version>) or constrain to a minor series (deeprelay_sdk~=0.<minor>.0, which allows patches only) and upgrade deliberately.
Breaking releases are called out in a ⚠ Breaking section of the GitHub Release notes for the release tag. At 0.x the version number alone will not warn you, so the release notes are the channel to read.
1.0.0 is a deliberate stability promotion, made as a human call; it never happens automatically by rolling over from 0.x.
§ 08.8Related¶
- TypeScript SDK: the same surface for Node.
- The deeprelay CLI: the command-line client.
- Inference API: OpenAI-compatible chat, embeddings, image and video generation.
- deeprelay-sdk on PyPI: the published package, with release history, metadata, and the full per-method reference in the README.