Skip to content
Usman Abbas edited this page Jun 8, 2026 · 5 revisions

Convert Python SDK

The Convert Experiences FullStack SDK for Python — server-side A/B testing, feature flags, and personalizations for Python 3.9+ applications (Django, Flask, FastAPI, and plain Python services).

The SDK is framework-agnostic and sync-first. You can reach your first experiment value in plain Python, with no web framework, and — using direct config — with no network call at all.

Source Repository: python-sdk

flowchart TD
    A0["Core (SDK entry point)"]
    A1["Context (per-visitor)"]
    A2["DataStore (visitor + dedup state)"]
    A3["evaluation.rules"]
    A4["evaluation.bucketing (MurmurHash3-32)"]
    A5["Transport (config fetch + tracking delivery)"]
    A6["evaluation.experiences"]
    A7["evaluation.features"]
    A8["TrackingQueue"]
    A9["EventBus / LifecycleEvent"]
    A10["ConfigSnapshot / SDKConfig"]
    A11["ConfigRefresher (opt-in daemon, Phase 2)"]
    A0 -- "Creates" --> A1
    A0 -- "Fetches config via" --> A5
    A0 -- "Owns" --> A8
    A0 -- "Fires events via" --> A9
    A0 -- "Spawns (opt-in)" --> A11
    A11 -- "Refreshes via" --> A5
    A11 -- "Atomically swaps" --> A10
    A1 -- "Runs experiments via" --> A6
    A1 -- "Runs features via" --> A7
    A1 -- "Reads/writes state via" --> A2
    A6 -- "Buckets via" --> A4
    A6 -- "Matches rules via" --> A3
    A7 -- "Buckets via" --> A4
    A7 -- "Matches rules via" --> A3
    A8 -- "Delivers via" --> A5
    A8 -- "Fires events via" --> A9
    A0 -- "Holds snapshot" --> A10
Loading

Hello, variation

The fastest path to a first bucketed variation uses direct config — a preloaded payload, no network call:

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},
            ],
        }
    ],
}

core = Core(SDKConfig(data=config_data)).initialize()
context = core.create_context("visitor-001")

result = context.run_experience("checkout-experiment")
if result is not None:
    print("Bucketed into:", result.variation_key)

core.close()

initialize() is synchronous and returns a ready Core. No callbacks, no async/await, no web framework required.

How the Python SDK differs from the JavaScript SDK

The Python SDK shares the same bucketing algorithm (pure-Python MurmurHash3-32, seed 9999) and the same config/targeting rule model as the JavaScript and PHP SDKs. The same visitor and experience always resolve to the same variation across all three. Python-specific adaptations:

  • Synchronous API. No Promises. Core(SDKConfig(...)).initialize() blocks until ready and returns. An async surface is planned for Phase 3 — see Async & Frameworks.
  • Typed frozen dataclasses. ExperienceResult, FeatureResult, and *Diagnostic types are stdlib frozen dataclasses with full type hints. SDKConfig, TransportConfig, and RefreshConfig are frozen dataclasses too — no dict-of-dicts configuration.
  • No separate tracking config. Queue settings (batch_size, auto_flush_interval_ms) are fields on SDKConfig directly. There is no TrackingConfig class.
  • Protocol-based extension. Transport, DataStore, and EventBus are @runtime_checkable Protocols (PEP 544). No subclassing needed — duck-type your own adapter.
  • Opt-in background refresh. Pass SDKConfig(refresh=RefreshConfig(...)) to enable a daemon refresh thread. No daemon thread runs by default.
  • Sync flush. core.flush() is synchronous. Call it at end-of-request, in a finally block, or via middleware. Auto-flush on a timer is opt-in via SDKConfig.auto_flush_interval_ms.

Getting started

Python SDK

Migration guides

How-to guides

Maintainers

Clone this wiki locally