llmtrack-sdk (Python)
LLMtrack records server-side LLM token usage, cost, and request context so teams can understand AI usage in one dashboard.
The PyPI distribution is named llmtrack-sdk, while Python imports use llmtrack_sdk: distribution names can contain hyphens, but import identifiers cannot, and the distinct name also avoids the unrelated llmtrack distribution.
Install
pip install llmtrack-sdk
Quickstart
Create an ingestion key in LLMtrack, save it at ~/.config/llmtrack/api-key, and run:
export LLMTRACK_API_KEY="$(cat ~/.config/llmtrack/api-key)"
import os
from llmtrack_sdk import LLMtrack
tracker = LLMtrack(api_key=os.environ["LLMTRACK_API_KEY"])
tracker.track(provider="openai", model="gpt-5.6-sol", prompt_tokens=241,
completion_tokens=86, reasoning_tokens=32, feature="support-chat")
Exported Node.js event type
When using the companion Node.js package, import its canonical class (with a lowercase t) and exported event type directly:
import { LLMtrack, type TrackOptions } from 'llmtrack';
const event: TrackOptions = { provider: 'openai', model: 'gpt-5.6-sol', promptTokens: 241, completionTokens: 86 };
Common mistakes
- Class casing: the canonical class is
LLMtrack, with a lowercaset. Python also exportsLLMTrackas a safety alias, but documentation and examples useLLMtrack. - Field casing: Python accepts snake_case event fields only (for example,
prompt_tokens); an unknown or wrong-cased key causes an unexpected-keywordTypeError, reported throughon_errorwhen usingtrack(). Node.js accepts camelCase only and silently drops unknown runtime properties (while TypeScript normally rejects them). - Provider usage: use
from_openai_usage()orfrom_anthropic_usage()rather than manually copying token counts and cache-accounting conventions. - Cost: do not supply
cost; client-supplied cost is ignored because LLMtrack computes cost server-side.
Provider examples
Both examples call track() immediately after the provider completion and read counts from the official response objects.
OpenAI
import os
from openai import OpenAI
from llmtrack_sdk import LLMtrack, from_openai_usage
openai, tracker = OpenAI(), LLMtrack(api_key=os.environ["LLMTRACK_API_KEY"])
response = openai.chat.completions.create(model="gpt-5.6-sol", messages=[{"role": "user", "content": "Explain why the sky is blue in two sentences."}])
tracker.track(provider="openai", model=response.model, **from_openai_usage(response.usage), feature="science-explainer")
Anthropic
Anthropic includes thinking tokens in output_tokens rather than exposing a separate reasoning-token count, so do not duplicate them in reasoning_tokens.
import os
from anthropic import Anthropic
from llmtrack_sdk import LLMtrack, from_anthropic_usage
anthropic, tracker = Anthropic(), LLMtrack(api_key=os.environ["LLMTRACK_API_KEY"])
response = anthropic.messages.create(model="claude-opus-5", max_tokens=1024, messages=[{"role": "user", "content": "Summarize the benefits of typed APIs."}])
tracker.track(provider="anthropic", model=response.model, **from_anthropic_usage(response.usage), feature="document-summary")
Cache accounting conventions
OpenAI-style usage is inclusive: prompt tokens already include cached input tokens, and completion tokens already include reasoning tokens. Anthropic-style usage is exclusive: input tokens exclude cache reads and writes reported separately. The adapters label these conventions automatically. Set cache_accounting yourself only when the server cannot infer the convention, such as for a gateway or an unrecognised vendor; use exactly inclusive or exclusive.
Fire-and-forget and awaited usage
| Method | Behavior |
|---|---|
track(**event) |
Synchronous fire-and-forget entry point: starts a daemon delivery thread, returns None immediately, never blocks and never raises. Errors go to on_error. |
await track_sync(**event) |
Async awaited entry point: returns the API dictionary and raises LLMtrackError on failure. Despite its compatibility name, it must be awaited. |
result = await tracker.track_sync(provider="openai", model="gpt-5.6-sol", prompt_tokens=241,
completion_tokens=86, reasoning_tokens=32, feature="support-chat")
Set enabled=False in tests and local development to make both methods no-ops:
tracker = LLMtrack(api_key=os.environ["LLMTRACK_API_KEY"], enabled=False)
Constructor options
| Name | Type | Default | Description |
|---|---|---|---|
api_key |
str |
required | Server-side LLMtrack ingestion key. |
base_url |
str |
https://llm-track.com |
API origin; primarily useful with a test server. |
environment |
str |
production |
Environment applied when an event does not override it. |
on_error |
Callable[[LLMtrackError], None] | None |
logs one [llmtrack] warning |
Receives track() background errors; callback exceptions are contained. |
on_warning |
Callable[[LLMtrackWarning], None] | None |
logs one [llmtrack] warning |
Receives pricing warnings, once per distinct warning per process. |
enabled |
bool |
True |
When False, both tracking methods do nothing. |
timeout_ms |
int |
5000 |
Timeout in milliseconds for each request attempt. |
max_retries |
int |
3 |
Maximum attempts, including the first request. |
Event fields
The wrapper accepts the following snake-case fields from the IngestRequest contract.
| Name | Type | Requirement | Description |
|---|---|---|---|
provider |
str |
required | Provider name, such as openai or anthropic. |
model |
str |
required | Provider model name, such as gpt-5.6-sol. |
prompt_tokens |
int |
required | Non-negative integer input-token count. |
completion_tokens |
int |
required | Non-negative integer output-token count. |
total_tokens |
int | None |
optional | Optional explicit total; when absent, the server computes it. |
reasoning_tokens |
int | None |
optional | Non-negative reasoning-token count when separately reported. |
cached_input_tokens |
int | None |
optional | Optional cached-input count. |
cache_write_tokens |
int | None |
optional | Optional cache-write count. |
input_audio_tokens |
int | None |
optional | Optional audio input-token count. |
output_audio_tokens |
int | None |
optional | Optional audio output-token count. |
cache_accounting |
str | None |
optional | inclusive or exclusive; overrides the inferred cache-accounting convention. |
latency_ms |
int | None |
optional | End-to-end latency in milliseconds. |
status |
str | None |
optional | One of success, error, timeout, or cancelled. |
feature |
str | None |
optional | Product feature, such as support-chat; defaults server-side to unknown. |
customer_id |
str | None |
optional | Your stable customer identifier. |
customer_name |
str | None |
optional | Your customer display name. |
environment |
str | None |
optional | Overrides the constructor environment for this event. |
metadata |
dict[str, Any] | None |
optional | JSON object containing request context; maximum serialized size is 8 KiB (8192 bytes). |
idempotency_key |
str | None |
optional | SDK-only header option used to deduplicate the call, not an event-body field. |
The client validates all token-count fields as non-negative integers, validates cache_accounting, and rejects metadata whose compact UTF-8 JSON serialization exceeds 8192 bytes. The server validates the remaining contract constraints.
Delivery, retries, and idempotency
Each call automatically receives a UUID v4 idempotency key. That key stays stable across retries, preventing double-counting when a stored response is lost. Supply your own stable key to deduplicate separate calls:
tracker.track(provider="openai", model="gpt-5.6-sol", prompt_tokens=241, completion_tokens=86,
reasoning_tokens=32, feature="support-chat", idempotency_key="9ea0c2ec-7b98-4bc3-9802-20ae6a468a35")
The SDK retries only network failures, timeouts, and HTTP 5xx responses. It never retries HTTP 4xx responses.
Errors
| Code | Meaning | Fix |
|---|---|---|
INVALID_API_KEY |
The key is missing, unknown, or belongs to a missing workspace. | Check LLMTRACK_API_KEY and create or copy a valid ingestion key. |
REVOKED_API_KEY |
The key was revoked. | Replace it with an active key. |
INACTIVE_API_KEY |
The key was deactivated. | Reactivate it or replace it. |
INVALID_PAYLOAD |
A field failed local or server validation. | Correct the named field; check integer token counts and metadata size. |
QUOTA_EXCEEDED |
The event allowance or pay-per-event credits are exhausted. | Wait for reset, add credits, or upgrade. |
PLAN_INACTIVE |
Billing status prevents ingestion. | Restore billing and reactivate the plan. |
NETWORK_ERROR |
Network/timeout retries were exhausted, or an unclassified HTTP/server failure occurred. | Check connectivity, base_url, timeout, and service availability. |
track_sync() raises these errors. track() sends them to on_error and never raises.
Warnings
| Response | Meaning |
|---|---|
pricing_status: unknown_model |
No active pricing matched the provider/model, so cost is 0; token usage is still recorded. |
Troubleshooting
Events are not appearing
A short-lived process can exit before the daemon thread finishes; use await track_sync() in scripts and jobs that must confirm delivery, and inspect its returned API dictionary.
Cost shows 0
Check for UNKNOWN_MODEL or pricing_status: unknown_model. Use the exact response.model returned by the provider so active pricing can match it.
The key is rejected
Inspect LLMtrackError.code: replace invalid/revoked keys, reactivate inactive keys, and confirm the environment variable is available to the server process. Use await track_sync() to inspect the complete exception.
Contributing
Async tests use pytest-asyncio (included in the test and dev extras):
pip install -e 'packages/python[test]'
pytest packages/python/tests
More information
Metadata
Release files for llmtrack-sdk 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| llmtrack_sdk-0.2.0.tar.gz | 49.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| llmtrack_sdk-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 145.7 kB
Release files / llmtrack_sdk-0.2.0.tar.gz
| Download URL | llmtrack_sdk-0.2.0.tar.gz |
|---|---|
| Size | 49.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e34feddf2d350a40db26c68cf6738dde8f614a3813bc6f5b8d76d73f69d2aa95
|
|
BLAKE2b-256 checksum How to use checksums |
35d02746686435dc990c10bd06b48d39370a09e152648881760f0fc9c1e0184d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.
Transparency logRelease files / llmtrack_sdk-0.2.0-py3-none-any.whl
| Download URL | llmtrack_sdk-0.2.0-py3-none-any.whl |
|---|---|
| Size | 96.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bc72a574e83d9e4eac5f3299c8cbc45b3859338548b74aba7c02ff86314313de
|
|
BLAKE2b-256 checksum How to use checksums |
b1aa9bdd5d985f104bf7c770fdc4eedc67e406f3c30f25d3403b6c90475632fa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.
Transparency log