Python Quickstart
Get running with the Python SDK in 5 minutes using direct config (no SDK key needed)
Get the Convert Python SDK running and bucket your first visitor in under 5
minutes. This guide uses direct config (an inline dict, no network call) so
it runs anywhere without an SDK key. A remote (sdk_key) variant is shown at
the end.
1. Install
pip install convert-python-sdkPython 3.9 or newer is required. httpx is installed automatically as the only
runtime dependency — it is only used when you initialize with an sdk_key. See
Installation for uv and Poetry alternatives.
2. Initialize with direct config (offline, no network)
Prefer direct config for local development, tests, and offline tooling. It makes no network call and is ready immediately.
from convert_sdk import Core, SDKConfig
config_data = {
"account_id": "100123",
"project": {"id": "200456"},
"experiences": [
{
"id": "e1",
"key": "checkout-experiment",
"variations": [
{"id": "v1", "key": "control", "traffic_allocation": 50.0},
{"id": "v2", "key": "treatment", "traffic_allocation": 50.0},
],
}
],
"goals": [
{"id": "g1", "key": "purchase_completed"},
],
}
core = Core(SDKConfig(data=config_data)).initialize()
assert core.is_readyinitialize() is synchronous and returns a ready Core. is_ready is True
once the config snapshot is loaded — reaching it already implies success.
3. Create a visitor context
Bind a visitor identity and optional attributes to the current config snapshot:
context = core.create_context(
"visitor-001",
visitor_attributes={"country": "US", "plan": "pro"},
)Attributes are copied defensively. Later mutations to the dict you pass do not
affect the context. Keep and reuse the returned context for the same visitor —
the SDK does not cache contexts for you.
4. Run an experience
run_experience returns a typed ExperienceResult when the visitor qualifies
and buckets into a variation, or None for any normal miss. It never raises for
ordinary no-match outcomes and performs no network I/O.
result = context.run_experience("checkout-experiment")
if result is not None:
print(result.experience_key) # "checkout-experiment"
print(result.variation_key) # "control" or "treatment"
print(result.variation_id) # "v1" or "v2"None is a normal outcome — it means the visitor did not qualify or the
experience was not found. No exception is raised.
5. Track a conversion
track_conversion records a goal conversion synchronously. It deduplicates by
(visitor_id, goal_id) and appends to an in-process queue. No network call
happens on track_conversion.
from convert_sdk import ConversionStatus
result = context.track_conversion("purchase_completed", revenue=49.99)
print(result.tracked) # True
print(result.status) # ConversionStatus.QUEUED
# A default duplicate for the same (visitor, goal) is suppressed:
again = context.track_conversion("purchase_completed")
print(again.tracked) # False
print(again.reason) # "deduplicated"
# Re-track with force_multiple for repeated revenue / transactions:
context.track_conversion("purchase_completed", revenue=10.0, force_multiple=True)An unknown goal key is a typed result (ConversionStatus.GOAL_NOT_FOUND), not
an exception.
6. Flush and close
Deliver queued events and release transport resources. Call flush() explicitly
at the end of a request, before process exit, or in a finally block:
core.flush() # delivers queued tracking events synchronously
core.close() # releases transport resourcesThere is no auto-flush by default — see Configuration for the
batch_size and auto_flush_interval_ms options and
Tracking Conversions for per-runtime patterns.
Complete direct-config example
from convert_sdk import Core, SDKConfig, ConversionStatus
config_data = {
"account_id": "100123",
"project": {"id": "200456"},
"experiences": [
{
"id": "e1",
"key": "checkout-experiment",
"variations": [
{"id": "v1", "key": "control", "traffic_allocation": 50.0},
{"id": "v2", "key": "treatment", "traffic_allocation": 50.0},
],
}
],
"goals": [
{"id": "g1", "key": "purchase_completed"},
],
}
core = Core(SDKConfig(data=config_data)).initialize()
context = core.create_context(
"visitor-001",
visitor_attributes={"country": "US", "plan": "pro"},
)
result = context.run_experience("checkout-experiment")
if result is not None:
print("Variation:", result.variation_key)
conversion = context.track_conversion("purchase_completed", revenue=49.99)
assert conversion.status is ConversionStatus.QUEUED
core.flush()
core.close()Remote initialization (sdk_key)
sdk_key)For production use, fetch live config from the Convert CDN by providing your SDK key. Read the key from the environment — never hard-code credentials:
import os
from convert_sdk import Core, SDKConfig
core = Core(SDKConfig(sdk_key=os.environ["CONVERT_SDK_KEY"])).initialize()
context = core.create_context("visitor-001")
result = context.run_experience("checkout-experiment")
if result is not None:
print("Variation:", result.variation_key)
core.flush()
core.close()initialize() fetches config over HTTPS (using the httpx-backed transport)
and raises ConfigLoadError if the fetch fails. All subsequent evaluation calls
are local and require no network.
Context manager
Core is a context manager. Using it as one is the recommended pattern for
scripts and short-lived code because close() is called automatically on exit,
even if an exception is raised inside the block:
from convert_sdk import Core, SDKConfig
with Core(SDKConfig(data=config_data)).initialize() as core:
context = core.create_context("visitor-001")
result = context.run_experience("checkout-experiment")
if result is not None:
print("Variation:", result.variation_key)
# flush() and close() called automatically on exitKey points
initialize()is synchronous. It blocks until the SDK is ready. There is no callback orasync/awaitin the public API.Noneis a normal outcome.run_experience()andrun_feature()returnNonewhen a visitor does not qualify — never raise.- No auto-flush by default. Queued tracking events are held in-process
until you call
core.flush(), thebatch_sizethreshold is hit (default 10), or the optionalauto_flush_interval_mstimer fires. - Direct config = no network. Passing
data=toSDKConfignever makes a network call — safe for tests, CI, and offline tooling. - One runtime dependency.
httpx>=0.28,<1.0is the only runtime dependency, and it is only used insdk_keymode.
Next steps
- Installation — Python version matrix, all install methods, import cheat-sheet
- Initialization —
SDKConfigoptions, remote vs. direct config,RefreshConfig - Configuration — full options reference
- Running Experiences — evaluation deep-dive
- Running Features — feature flag resolution and typed variables
- Tracking Conversions — deduplication, revenue, per-runtime flush patterns
- Code Examples — all SDK methods with examples