Python SDK
Use Enlitt from a Python backend such as FastAPI, Starlette, Django, or Flask.
The Python SDK, enlitt on PyPI, does what the Node.js SDK does on a Python
server:
- login handlers and a session middleware;
- standard calls through an OpenAI-shaped client;
with_premium_charge()around any provider call;usage();- the same typed errors.
Every rule on the other pages applies unchanged. That includes login, premium limits, idempotency, and errors. This page shows only the Python names.
Install
The SDK needs Python 3.10 or later. It is async only, so every call is awaited.
pip install "enlitt[openai,starlette]"
# or
uv add "enlitt[openai,starlette]"Each extra adds a dependency:
openaiadds the officialopenaipackage, for the OpenAI-shaped client.starletteadds the FastAPI and Starlette routes.
The core needs only httpx.
Then wire the project:
npx enlitt setup --name "My Python app"The CLI needs Node.js. setup finds pyproject.toml or requirements.txt. It
then writes ENLITT_CLIENT_ID and ENLITT_SECRET_KEY to .env and records the
application in enlitt.json. Keep .env out of git; npx enlitt doctor
reports it when it is committed. Load .env with your usual tool, such as
python-dotenv or your process manager. The SDK reads ENLITT_SECRET_KEY from
the environment.
Mount login and the session middleware (FastAPI)
import os
from fastapi import FastAPI
from enlitt.asgi import EnlittSessionMiddleware
from enlitt.auth import CallbackConfig, StateRecord
from enlitt.starlette import enlitt_routes
class MemoryStateStore:
"""Development only. Use a shared store (Redis, your database) when you run more than one process."""
def __init__(self) -> None:
self._records: dict[str, StateRecord] = {}
async def set(self, state: str, record: StateRecord, ttl_ms: int) -> None:
self._records[state] = record
async def get(self, state: str) -> StateRecord | None:
return self._records.get(state)
async def consume(self, state: str) -> StateRecord | None:
return self._records.pop(state, None)
config = CallbackConfig(
client_id=os.environ["ENLITT_CLIENT_ID"],
app_origin="https://your-app.example",
redirect_uri="https://your-app.example/enlitt/callback",
default_return_to="/",
state_store=MemoryStateStore(),
# secret_key defaults to the ENLITT_SECRET_KEY environment variable.
)
app = FastAPI(routes=enlitt_routes(config=config))
app.add_middleware(EnlittSessionMiddleware)enlitt_routes() mounts three routes:
- login start at
/enlitt/login; - the callback at
/enlitt/callback, which must be the path ofredirect_uri; - logout at
/enlitt/logout.
Each takes a keyword argument to change its path. The handlers behave exactly as
described on the login page, and StateRecord and
StateStore have the same meaning there.
Django and Flask
The handlers are plain async functions over a small HandlerRequest and
HandlerResponse, so any async framework can mount them. In Django, convert in
an async view:
from django.http import HttpResponse
from enlitt.auth import HandlerRequest, login_start_handler
login = login_start_handler(config)
async def enlitt_login(request):
response = await login(
HandlerRequest(
method=request.method,
url=request.build_absolute_uri(),
headers={k.lower(): v for k, v in request.headers.items()},
body=request.body,
)
)
out = HttpResponse(response.body, status=response.status)
for name, value in response.headers:
out.headers[name] = value # use out.cookies for several set-cookie values
return outTo make a model call from a sync view, wrap it in the session yourself:
from enlitt import enlitt_session, run_with_enlitt_session
token = request.COOKIES.get("enlitt_session")
session = enlitt_session(token) if token else None
result = await run_with_enlitt_session(session, lambda: make_the_call())Flask works the same way with flask[async].
Make a standard call
The client mirrors the OpenAI Responses API. Call it inside a request that has gone through the middleware:
from enlitt.openai import ENLITT_STANDARD_MODEL, enlitt
client = enlitt()
@app.post("/api/summary")
async def summary() -> dict[str, str]:
response = await client.responses.create(
model=ENLITT_STANDARD_MODEL,
input="Summarize this support ticket.",
)
return {"text": response.output_text}Pass stream=True to get an async iterator of OpenAI stream events. To cancel
a call, cancel the task or close the iterator. The standard model's limits
are on the models page; unsupported options raise
EnlittConfigurationError before any request is sent.
Make a premium call
Wrap your own AsyncOpenAI client. Your provider key stays on your server:
from openai import AsyncOpenAI
from enlitt.openai import EnlittCallOptions, enlitt, per_token
premium = enlitt(AsyncOpenAI())
response = await premium.responses.create(
model="gpt-4.1-mini",
input="Write a long story.",
enlitt=EnlittCallOptions(
max_charge="20000000", # NANOUSD, as a string
price=per_token(input="400", output="1600"),
),
)enlitt= is never sent to the provider. Amounts are always canonical integer
strings in NANOUSD.
For any other provider (images, voice, another model SDK), use
with_premium_charge():
from enlitt import PremiumProvider, WithPremiumChargeOptions, with_premium_charge
async def run(charge, operation):
image = await my_image_provider.generate(prompt)
charge("5000000") # what this call costs the user, in NANOUSD
return image
image = await with_premium_charge(
WithPremiumChargeOptions(
max_charge="5000000",
provider=PremiumProvider(id="my-image-provider", model="image-v2"),
),
run,
)The call is reported exactly once. If run returns, it is reported as
completed; if it raises, as failed; if it is cancelled, as cancelled.
Remaining capacity
from enlitt import usage
capacity = await usage()
print(capacity.standard.remaining_percent, capacity.premium.limit_cap_nano_usd)Errors
The error classes have the same names as in Node.js: EnlittError,
EnlittLoginRequiredError, EnlittInsufficientCreditsError,
EnlittLimitExceededError, EnlittSubscriptionInactiveError,
EnlittRateLimitError, EnlittAuthenticationError,
EnlittAuthorizationError, EnlittConfigurationError, and
EnlittModelError. Each carries code, provider_invoked, retryable,
retry_after_ms, request_id, and authorize_url. The errors page
says which ones are safe to show a user.
from enlitt import EnlittLoginRequiredError
try:
response = await client.responses.create(model=ENLITT_STANDARD_MODEL, input=prompt)
except EnlittLoginRequiredError:
return RedirectResponse("/enlitt/login?return_to=/chat", status_code=303)Games and native apps
If your Python server backs a game or a desktop or mobile app, mount the
client sign-in routes with
enlitt_routes(config=config, client_sign_in=...). Then add the middleware
with EnlittSessionMiddleware(app, header=True), so the game's
Enlitt-Session header is read.