Skip to main content
The Zespan Python SDK instruments your Python LLM application with a single init() call. It uses a background daemon thread to flush events without blocking your application, and registers an atexit handler to flush on process exit.

Installation

Initialization

Call zespan.init() once at startup before making any LLM calls. All parameters are keyword arguments.
Parameters:
string
required
Your Zespan API key. Must start with zsp_. Find this in your project settings.
string
default:"production"
Environment label attached to every event. Use "staging" or "development" to separate traces by environment, or any custom string that matches an environment you’ve created (Settings → Environments) in the dashboard.A value that doesn’t match any environment on the project (and isn’t "production"/"development", which alias to the seeded prod/dev) is never dropped — it’s still stored under the string you sent, with a warning logged on the project (throttled to once an hour). See Environments for the full alias and asymmetry behavior.
boolean
default:"True"
When True (default), prompt and completion text are stored alongside traces with PII redaction applied before transmission. Set to False to disable prompt storage entirely. For Google ADK, False also omits tool arguments, tool results, decision rationale, and error text — see Google ADK.
boolean
default:"True"
Capture v2 is on by default (SDK 1.7.0 and later). It records tool results (redacted JSON, up to 32,768 characters, with result size and emptiness), tool definitions, the structured messages and system prompt a model saw, requested tool calls, the output schema and agent input and output text, each with a content-free zs_capture marker saying how the field was captured. Redaction stays always on, and store_prompts and store_tool_io still decide what is stored: with either off, that content is not recorded and the marker says off.Turn it off with capture_v2=False or the environment variable ZESPAN_CAPTURE_V2=off. The environment variable wins over the option, and ZESPAN_CAPTURE_V2=on forces it on. With it off, events are exactly what earlier versions sent. A one-time notice is logged on the first event when it is on only because of the default; it never includes event content, and setting the option or the variable explicitly silences it. Image metadata (capture_image_meta) is a separate opt-in and stays off by default; capture_tool_definitions and capture_agent_io stay on. Events can be larger, and requests are capped at about 900 KB, so tool-heavy apps send more requests. See Capture coverage.
float
default:"1.0"
Fraction of events to send, between 0.0 and 1.0. Set to 0.1 to trace 10% of calls.
list[str]
Keys whose values are redacted before any data is stored. Applied regardless of store_prompts. Passing this option replaces the default list rather than adding to it — see PII redaction below.
boolean
default:"False"
Needs capture v2, which is on by default. For each image in a model request, records only metadata on the model call (also a failed one): the declared type, the type read from the first bytes, the size, whether the base64 text is valid, width and height for PNG, JPEG, GIF and WebP, the URL scheme and host (never the path or query), whether a pre-signed link had already expired, a SHA-256 for images up to 256 KiB, and the tool call that produced the image. It never records the image itself. With store_prompts off the host and the hash are left out. Also on with ZESPAN_CAPTURE_IMAGE_META=1; ZESPAN_CAPTURE_IMAGE_META=0 always turns it off. See Capture fields.
boolean
default:"True"
On by default (SDK 1.7.0 and later); pass False to turn it off. Pattern-based PII detection (emails, phones, cards, IBANs, SSNs, and similar) replaces matches with keyed tokens such as [EMAIL_3F9A2C1B] in your process, layered on top of key-based redact_keys matching. Tune it with pii_redaction_mode, pii_whitelist and pii_categories; set redaction_key to share tokens across services.
dict
default:"None"
For calls that go through an AI gateway which also exports its own traces to Zespan: {"hosts": ["gateway.example.com"]}. For a client whose base URL host is listed (exact name, or a leading dot like .internal; for LiteLLM, the call’s api_base), the patched OpenAI, Anthropic and LiteLLM calls add traceparent (plus cf-aig-otel-trace-id and cf-aig-otel-parent-span-id) to extra_headers, so a gateway that honours them nests its span under the SDK span and Zespan can count the call’s tokens and cost once (when merging is on, see the linked section). A header you set yourself is never replaced. Nothing is added unless you set this. Every LLM event of these wrappers also records the provider’s response id as gen_ai.response.id. See Using the SDK too.
int
default:"50"
Number of events to accumulate before flushing.
float
default:"2.0"
Seconds between automatic flushes. The SDK also flushes on process exit.
int
default:"10000"
Maximum number of events held in the in-memory queue (the default was 1,000 before SDK 1.7.0). Once exceeded, the oldest queued event is dropped to make room for new ones, and counted. See Delivery, retries and lost events.
int
default:"20971520"
Maximum bytes of encoded events held in the in-memory queue (20 MiB). Whichever of max_queue_size and max_queue_bytes is reached first drops the oldest event. An event whose size estimate is over this limit is dropped when queued (counted as too_large) without being trimmed. Values below 1,000,001 also drop an event whose size check could not finish.
float
default:"30.0"
Timeout for each request to the ingest API, in seconds. Events from a timed-out request are kept and retried. See Delivery behaviour.
string
default:"https://api.zespan.com/v1/ingest"
Full ingest URL. Prompts, datasets, and guardrail checks all derive their base URL from this value. Override it for self-hosted deployments.
string
default:"https://api.zespan.com"
Root API URL used only by the config-propagation client (see Config propagation). For a self-hosted deployment, set this to the same host as endpoint.
string
Your Zespan project ID. Required, together with enable_zespan_pilot=True, to receive live config changes pushed from ZespanPilot or the dashboard.
boolean
default:"False"
When True and project_id is set, the SDK applies config changes (model overrides, sample rate, guardrail toggles) pushed from ZespanPilot or the dashboard. See Config propagation.
boolean
default:"False"
When True, also exports spans to an OpenTelemetry-compatible backend. Requires otel_endpoint.
string
OTel collector endpoint URL. Required when enable_otel=True.
string
default:"zespan-sdk"
Service name attached to exported OTel spans.
boolean
default:"False"
When True, logs internal flush errors to stdout. Enable during integration testing.

Auto-patching all providers

autopatch() detects which LLM libraries are installed and patches them all in one call. Use this instead of calling individual patch functions.
Covers: OpenAI, Anthropic, Google Generative AI (legacy google-generativeai package), AWS Bedrock, Mistral, Groq, LiteLLM — whichever of these packages are importable in your environment.
autopatch() does not patch the new Google GenAI SDK (google-genai) or OpenRouter. Call zespan.patch_google_genai() or zespan.patch_openrouter() explicitly for those.

Provider patches

OpenAI

Anthropic

Google Generative AI (legacy SDK)

Also patches genai.embed_content() automatically — embedding calls emit span_kind: embedding. Image generation models (e.g. gemini-3.1-flash-image) are detected from the response and emit span_kind: image_gen. The google-genai package is Google’s current Python SDK. patch_google_genai() patches Client.__init__ so every client instance you create is automatically traced.

AWS Bedrock

Mistral

Groq

LiteLLM

OpenRouter

Framework and agent integrations

Beyond the direct provider patches above, the SDK integrates with popular agent and orchestration frameworks. Each has its own dedicated guide:
  • LangChain — ZespanCallbackHandler traces chains, agents, tools, and retrievers. See LangChain.
  • LlamaIndex — ZespanLlamaIndexCallbackHandler traces query engines and retrievers. See LlamaIndex.
  • AutoGen / AG2 — wrap_autogen_agent traces ConversableAgent replies. See AutoGen.
  • CrewAI — wrap_crew traces crew task execution. See CrewAI.
  • Google ADK — wrap_adk_agent (or ZespanADKTracer) traces ADK agent runs. See Google ADK.
  • Haystack — ZespanHaystackTracer traces pipeline components. See Haystack.
  • Semantic Kernel — instrument_semantic_kernel traces kernel function invocations. See Semantic Kernel.

Context enrichment

Use with_zespan_context to attach a user_id, session_id, or custom tags to all traces generated within a function scope.
Set user_id and session_id on every request that involves a logged-in user. This enables per-user cost breakdown and session replay in the Zespan dashboard.
You can later erase or export everything Zespan holds for one end user by this user_id or session_id — see End-user data requests. Use a unique session_id per conversation: erasing a user also removes every session they appeared in.

Agent tracing with with_agent

Use the with_agent context manager to trace a multi-step agent workflow. It creates an agent span and exposes methods to log plans, trace tool calls, and record handoffs to other agents.
with_agent parameters:
string
required
Display name for this agent in traces and the agent registry.
string
default:"specialist"
Role label such as "coordinator", "specialist", or "planner".
string
default:"custom"
Framework name, e.g. "custom", "langchain", "google-adk".
object[]
List of tool definition objects with name and description fields.
AgentContext methods:
  • agent.log_plan(steps: list[str]) — records a planning span
  • agent.trace_tool(name, args, callable) — wraps a callable, records args and return value as a tool span
  • agent.delegate_to(target_name, reason) — records a handoff span

Manual spans with start_span

Use start_span to instrument any function as a custom span and attach evaluation scores. It returns a ManualSpan — enter span.run() to propagate trace context to nested calls, then call span.end() when the work completes to emit the event.

Prompt management

The PromptClient fetches versioned prompts from the Zespan prompt library at runtime. Results are cached locally for 5 minutes.
See the Prompt management page for the full API reference — the Python PromptClient exposes the same methods: get, list, create, update_labels, compile, and clear_cache. get() also accepts a fallback dict so calls degrade gracefully if the prompt library is unreachable, and compile() accepts a placeholders dict for prompts that splice in reusable message-list segments.
In Python, method and parameter names use snake_case: update_labels, clear_cache, prompt_type, commit_message, placeholders, fallback.

Dataset experiment runs

Use DatasetsClient (client.datasets) to run your own pipeline against a Zespan dataset, link each result back to Zespan via the trace ID your code already produces, then score and compare runs in the dashboard.
DatasetsClient methods:
method
Lists all of a dataset’s items by dataset name, fetching page after page until the last one. Stops with a warning after 100 pages; for very large datasets, use iter_items.
method
A generator over a dataset’s items that fetches one page at a time, so you can process large datasets without holding every item in memory.
method
Creates or fetches a named run against a dataset. Idempotent — safe to call every time a job starts. Returns a run handle.
Run handle methods:
Links a dataset item to this run via a trace ID your own code already produced. Pass observation_id to link a specific span instead of the whole trace.
This links raw traces to a run — it doesn’t score or compare anything itself. See Datasets for the full dashboard-side walkthrough of scoring linked runs and comparing them against each other.

Delivery behaviour

Events are sent by a background thread, so tracing never blocks or raises in your code. On failure the SDK keeps and retries your events:
  • No duplicates on retry. Each batch is sent with an automatic Idempotency-Key that is reused on every retry, so a lost response can’t ingest the same events twice.
  • At most 100 events per request. Larger backlogs are sent as several requests, and a batch the server rejects as too large is split in half.
  • Timeouts. Requests time out after 30 seconds (request_timeout). Events from a timed-out request are kept and retried.
  • Retry-After is honoured, up to 60 seconds. A persistent 429 or 503 means your events are being kept and retried, not lost.
  • Bounded queue. While delivery is failing, events wait in memory (up to max_queue_size, 1,000 by default). When it’s full, the oldest events are dropped.
  • Unacceptable batches are dropped. A batch the server will never accept — for example, one with no valid events — is dropped with an error log so it doesn’t block newer events. A 401 or 403 keeps your events and backs off.
  • Safe serialisation. datetime and similar values are converted to strings, circular references become [Circular], and NaN and infinite numbers become null. An event that still can’t be serialised is dropped on its own.
  • Exit flush. When your process exits, the SDK makes one delivery attempt per batch within a 10-second total budget. Events not delivered in that time are dropped, so call flush() before exiting when you need delivery guarantees.

Flushing in serverless environments

In short-lived processes such as AWS Lambda, Vercel Functions, or Cloud Run, call zespan.get_client().flush() explicitly before the handler returns to guarantee delivery.
Omitting zespan.get_client().flush() in serverless environments is the most common cause of missing traces. Always call it before your handler returns.

PII redaction

Zespan automatically redacts values from tags and metadata fields before they leave your application. The key is preserved; the value is replaced with "[REDACTED]". Default redacted keys (applied whenever redact_keys is not passed at all): password, secret, token, api_key. Pass your own redact_keys list at initialization to protect additional fields:
redact_keys replaces the default list rather than adding to it. If you pass your own list, include password, secret, token, and api_key explicitly if you still want them redacted.
Key-based redaction applies to tags and metadata fields, and to stored prompt and completion text. Prompt storage is on by default — set store_prompts=False to disable it entirely.

Pattern-based PII detection

redact_pii is on by default (SDK 1.7.0 and later); pass redact_pii=False to turn it off. Emails, phones, cards (Luhn), IBANs, SSNs, national ids, IPs, street addresses and dates of birth are replaced before the event is sent, on the flush thread rather than your call stack. With pii_redaction_mode="placeholder" (the default) the replacement is a keyed token such as [EMAIL_3F9A2C1B]: the same value gives the same token inside one trace under one key (this process, or every service sharing redaction_key), and it is not reversible. Tokens made by the server use the server’s key, so they differ from the SDK’s; Zespan’s checks treat tokens from different keys as unknown, never as different values. Secrets and sensitive keys are always removed, and the server also redacts content fields (identity fields such as user_id are stored as sent). See PII redaction for tokens, the CPU budget and the 32,768-character tool payload cap.
string
Secret that keys the PII tokens. Default: a random key per process. Set the same value in every service (TypeScript: redactionKey) so a trace that crosses services gets equal tokens. Use a random secret of 32 bytes or more, and never log it. It is read once and not kept on the client, and never logged or sent.
It applies to every integration and also covers error_message, tags and metadata string values, tool results and tool definitions. If pattern detection fails, the key-redacted value is sent and a single RuntimeWarning is logged; if key redaction fails for a field, it is sent as [REDACTION_FAILED]. Options on top of key-based redact_keys matching:
list[str]
Explicit list of PII categories to detect: email, phone, credit_card, ssn, ip, iban, national_id, address, dob. Leave empty to detect all of them.
string
default:"placeholder"
How matched values are redacted: "placeholder" (replace with a keyed token such as [EMAIL_3F9A2C1B]), "mask-middle", or "mask-all".
list[str]
Values that should never be redacted even if they match a PII pattern.
string
Accepted for cross-SDK compatibility; it does not affect matching in Python. Use pii_categories.
float
default:"0.7"
Accepted for cross-SDK compatibility; it does not affect matching in Python.

Guardrails

Guardrails run content safety checks before sending a prompt to the LLM (pre-check) and before returning the completion (post-check). Pass guardrails=True to any patch function to enable both phases.
See Guardrails for the full reference — parameter defaults, all fields, the direct check_guardrails() API, and a multi-wrapper example.

Outcomes

zespan.outcome(...) reports a business result (a deflected ticket, an avoided refund) back to a trace, from your own backend and usually after the trace ended. It sends one request to /v1/ingest/outcomes, never through the batch queue, and raises zespan.OutcomeError (and nothing else) when it fails.
See Outcomes for every argument, the error codes and the TypeScript equivalent.

Report changes

zespan.change(...) tells Zespan that a deploy, prompt, model, config, flag or dependency changed. It sends one small request with metadata only (kind, service, environment, a short ref, a version label, a time and attributes you choose) and never raises: it returns a ChangeResult(ok, id, replayed, reason).
It reads ZESPAN_CHANGE_KEY (a write-only change key) first, then your SDK key, so a CI secret can only report changes. ZESPAN_CHANGES=off sends nothing, and timeout_s (default 5) sets the wait. Reporting the same release twice stores one change. See Report changes from your CI for every field, the limits and the CLI and pipeline snippets.

Config propagation

Zespan can push configuration changes — model overrides, fallback models, retry/timeout policies, sample rate, guardrail toggles, and more — to your running application without a redeployment. Changes made via ZespanPilot or the dashboard are picked up on the next event flush (default every 2 seconds), as long as both project_id and enable_zespan_pilot=True are set at init:
See Config propagation for the full list of rule types and the programmatic ConfigClient API.

Delivery, retries and lost events

The SDK sends events in the background and is built so that a slow or failing Zespan API never blocks or breaks your app. These are the rules it follows. They are the same in the TypeScript and Python SDKs (Python 1.7.0 and later).
An event whose size estimate is over max_queue_bytes is dropped when queued, without trimming. Setting max_queue_bytes below 1,000,001 also drops events whose size check could not finish (an extremely large or deeply nested value), because those are estimated at 1,000,001 bytes or more. Leave it at the default unless you need a smaller memory limit.
Python SDK: a numpy array with more than 1,000 items in an event field (such as tool arguments or metadata) is sent as a short summary string, <ndarray shape=(...) dtype=...>, instead of numpy’s own text. Arrays of 1,000 items or fewer are unchanged. An array is charged its real memory size against the queue byte budget.

Lost-event reports

Every dropped event is counted by reason. The SDK sends those counts, and nothing else, to POST /v1/sdk/client-reports, so Zespan can show you how many events were lost in your app before they arrived (see Events lost in the app). A report holds a random report id, the SDK name and version, and a list of {reason, events} counts. It never contains event content, trace ids, hostnames or error messages.
  • Reports start after the first successful send. When events were lost, a report goes out about once a minute (a 429 with Retry-After can change the next attempt). An empty heartbeat goes out once an hour, so the dashboard can tell “none lost” from “no report received”.
  • A report that fails is retried with the same id, so it is never counted twice; one still undelivered after 55 minutes is dropped. A failing report is not counted as a lost event and does not stop events from being sent.
  • A self-hosted API without the route answers 404; the SDK then tries again only at the hourly cadence.
  • During normal running the report is sent from the background flush thread (never your calling thread), so a report request that times out can delay one flush by up to 5 seconds. The final report at shutdown runs on the thread that calls shutdown(), within its time budget.

Next steps

Instrumentation

See which inputs your traces recorded, and the step that fixes any that did not.

Events lost in the app

How lost-event counts reach the dashboard.

PII redaction

Keyed tokens, sharing a key across services, and the 32,768-character tool payload cap.