How to · Python

LLM Cost Tracking Per User in FastAPI, the Clean Pattern

By Ghiles Asmani, founder of Weckr · Published August 15, 2026

Short version: attach at the endpoint, where your auth dependency has already resolved the user and the provider call happens anyway. Module level Weckr client, wk.chat(client, params) with user_id, feature, and plan inside the params dict, and margins accumulate per user with caps enforced before each call. The Python SDK adds zero dependencies, and an MIT starter exists with the loop wired around a Claude endpoint.

The pattern

# app/weckr.py - module level, once
import os
from openai import OpenAI
from weckr import Weckr

openai_client = OpenAI()
wk = Weckr(
    api_key=os.environ["WECKR_API_KEY"],
    plans={"free": 0, "pro": 29},
)
# app/routes/ai.py
from fastapi import APIRouter, Depends
from app.weckr import wk, openai_client
from app.auth import current_user

router = APIRouter()

@router.post("/summarize")
async def summarize(body: SummarizeIn, user=Depends(current_user)):
    result = wk.chat(openai_client, {
        "model": "gpt-5.4-mini",
        "messages": [{"role": "user", "content": prompt(body.text)}],
        "user_id": user.id,          # snake_case in Python
        "feature": "summarize",
        "plan": user.plan,
    })
    return {"summary": result.choices[0].message.content}

Note the shape: per call metadata lives inside the params dict alongside model and messages, mirroring the TypeScript SDK. The endpoint doubles as your API key proxy, the architecture every AI app needs anyway per the proxy pattern, so tracking rides infrastructure you already owe yourself. Anthropic, Gemini, and Kimi clients wrap identically, and cap behavior (block raises WeckrCapError, downgrade substitutes silently) matches the decision guide in block or downgrade.

Python specifics worth knowing

  • Zero runtime dependencies: the SDK uses only the standard library, so your dependency tree and audit surface stay unchanged.
  • Long lived workers: fire and forget logging just works under uvicorn/gunicorn. Short lived processes (scripts, exiting workers, serverless) should flush before exit.
  • Agent frameworks: LangChain and CrewAI code wraps at the same boundary, the underlying client, per the LangChain guide and the CrewAI guide.

Or clone it working

weckr-fastapi-starter is MIT licensed: FastAPI, a Claude endpoint, SQLite, and Weckr wired through exactly this pattern. Run it, hit the endpoint once, and watch the margin row appear, the full loop before you touch your own code.

FAQ

How do I track LLM costs per user in a FastAPI app?

In the endpoint where you call the provider, with the authenticated user from your dependency in scope. Construct the Weckr client once at module level with your key and plan prices, wrap calls with wk.chat passing user_id, feature, and plan (snake_case in Python), and each call logs with server side cost recompute and margin against the plan. The provider response returns unchanged.

Does the Python SDK add dependencies to my FastAPI project?

No. weckr-sdk is deliberately dependency free at runtime, it uses only the Python standard library internally, so pip install weckr-sdk adds nothing else to your environment. You bring your own provider SDK (openai, anthropic, or google-genai) exactly as you already do.

How do async FastAPI endpoints interact with the logging?

Logging is fire and forget after the provider call resolves, so it does not block your response. In long lived servers (uvicorn or gunicorn workers) that is all you need. In short lived contexts, scripts, workers that exit, or serverless deployments, flush before exiting so in flight logs are not dropped with the process.

Is there a FastAPI starter with this wired already?

Yes: weckr-fastapi-starter on GitHub, MIT licensed, with FastAPI, a Claude endpoint, SQLite, and Weckr tracking cost and margin per user already integrated. It is the quickest way to see the whole loop, request in, Claude call, margin row in the dashboard, before wiring your own app.

Do spending caps work the same from Python?

Identically: the SDK checks the user’s month to date spend against their plan’s cap before the call, raising WeckrCapError on block or silently substituting the cheaper model on downgrade, with the same fail open behavior on network errors. The two SDKs are wire compatible, same backend, same dashboard, same caps.

Keep reading

Two lines into the endpoint you already have

The user, the call, and the plan are already in your endpoint’s scope; the margin layer is the wrapper between them. Free for 50,000 requests a month, Python docs at useweckr.com/docs/python, end state visible on the demo.

See the dashboard with real data, no signup needed.

Try the demo →