Skip to content
  • There are no suggestions because the search field is empty.

Python Quickstart

Get Running with the Convert Python SDK in 5 Minutes

THIS ARTICLE WILL HELP YOU:

Overview

The Convert Python SDK lets you run Convert Full Stack experiments and feature flags directly from Python applications.

It is designed for backend experimentation where your Python application decides which experience, feature, API response, pricing logic, or business workflow a visitor should receive before a response is returned.

Unlike browser SDKs, all visitor evaluation happens locally after the configuration has been loaded.

This Quickstart uses Direct Configuration, which requires no SDK Key and performs no network requests, making it ideal for:

  • local development
  • automated tests
  • CI pipelines
  • offline tooling
  • development environments

For production deployments, the SDK can instead download live configuration from the Convert CDN using your SDK Key.

Convert Full Stack projects allow experimentation across backend and frontend systems, enabling optimization beyond browser-based experiences.

1. Install the Python SDK

Install the SDK using pip:

pip install convert-python-sdk

Python 3.9 or newer is required.

The SDK installs httpx automatically as its only runtime dependency.

httpx is only used when the SDK is initialized with an SDK Key to download remote configuration.

Direct configuration mode performs no HTTP requests.

Alternative installation methods such as Poetry and uv are also supported.

Requirements

Requirement Version
Python 3.9+
Runtime dependency httpx
Network required Only when using sdk_key

2. Initialize with Direct Configuration

Direct Configuration allows the SDK to run completely offline.

Instead of downloading configuration from Convert, you provide it directly as a Python dictionary.

This makes the SDK:

  • deterministic
  • immediately ready
  • ideal for tests
  • ideal for local development

Example:

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_ready

Unlike the mobile SDKs, initialization is fully synchronous.

initialize() blocks until the SDK is ready.

Once initialization completes successfully:

core.is_ready

returns:

True

There is:

  • no callback
  • no Promise
  • no async/await
  • no background readiness event

Key Points

Direct Configuration makes no network requests

Providing:

SDKConfig(data=config_data)

loads configuration directly from memory.

No HTTP request is performed.

This makes Direct Configuration safe for:

  • unit tests
  • integration tests
  • CI
  • offline environments
  • local development

initialize() is synchronous

Initialization blocks until configuration is loaded.

Unlike the Android or iOS SDKs, there is no asynchronous readiness callback.

core = Core(
    SDKConfig(data=config_data)
).initialize()

When this returns successfully, the SDK is ready for evaluation.

3. Create a Visitor Context

After the SDK is initialized, create a visitor context.

context = core.create_context(
    "visitor-001",
    visitor_attributes={
        "country": "US",
        "plan": "pro"
    }
)

A visitor context combines:

  • visitor identity
  • visitor attributes
  • current configuration snapshot

Every evaluation happens against this context.

The SDK does not cache contexts.

Reuse the returned context while processing the same visitor.

Visitor Attributes

Visitor attributes allow experiences to target visitors based on custom information.

Example:

visitor_attributes={
    "country": "US",
    "plan": "pro"
}

The SDK defensively copies the attribute dictionary.

Any changes made to the original dictionary after calling create_context() do not affect the visitor context.

Stable Visitor IDs

Use a stable visitor identifier whenever possible.

For authenticated users, this is typically your internal user ID.

For anonymous users, use a persistent identifier such as a first-party cookie or another stable anonymous identifier.

Stable visitor identifiers help maintain consistent experiment assignment while the configuration remains unchanged.

4. Running an Experience

Evaluate an experiment using its experience key.

result = context.run_experience(
    "checkout-experiment"
)

If the visitor qualifies, the SDK returns an ExperienceResult.

print(result.experience_key)
print(result.variation_key)
print(result.variation_id)

Example output:

checkout-experiment
control
v1

Handling Visitors That Do Not Qualify

A visitor that does not qualify simply returns:

None

Example:

result = context.run_experience(
    "checkout-experiment"
)

if result is not None:
    render_variation(result.variation_key)
else:
    render_default()

None is an expected result.

It is not an exception.

The SDK never raises an exception simply because a visitor did not qualify.

Server-Side Rendering Pattern

One common implementation is deciding the experience before rendering HTML.

Example:

result = context.run_experience(
    "checkout-experiment"
)

if result is None:
    return render_template(
        "checkout_default.html"
    )

if result.variation_key == "treatment":
    return render_template(
        "checkout_new.html"
    )

return render_template(
    "checkout_default.html"
)

This allows the visitor to receive the correct experience immediately, avoiding browser-side flicker.

Server-side experimentation is particularly useful for:

  • Flask
  • Django
  • FastAPI
  • Starlette
  • custom WSGI/ASGI applications

5. Tracking Conversions

Track a conversion synchronously.

result = context.track_conversion(
    "purchase_completed",
    revenue=49.99
)

Inspect the result:

print(result.tracked)
print(result.status)

Example:

True
ConversionStatus.QUEUED

Unlike browser SDKs, tracking a conversion does not immediately perform a network request.

Instead, the SDK appends the event to an in-process queue.

Duplicate Conversions

Conversions are automatically deduplicated by:

  • visitor ID
  • goal ID

Example:

again = context.track_conversion(
    "purchase_completed"
)

print(again.tracked)
print(again.reason)

Output:

False
deduplicated

This prevents accidental duplicate goal tracking.

Repeated Revenue Events

If you intentionally want to record another transaction:

context.track_conversion(
    "purchase_completed",
    revenue=10.0,
    force_multiple=True
)

The conversion remains deduplicated while the additional revenue transaction is recorded.

6. Flush and Close

Unlike some SDKs that automatically send tracking events in the background, the Python SDK keeps conversion events in an in-process queue until they are explicitly flushed.

Call:

core.flush()

to synchronously deliver queued tracking events.

When your application is finished using the SDK, release transport resources with:

core.close()

Example:

core.flush()
core.close()

Why Flush Matters

There is no automatic flush by default.

Queued events remain in memory until one of the following occurs:

  • core.flush() is called
  • the configured batch_size threshold is reached
  • auto_flush_interval_ms is configured and the timer fires

If your application exits before queued events are flushed, those events are lost.

This is especially important for:

  • CLI scripts
  • scheduled jobs
  • serverless functions
  • one-off migration scripts
  • test runners

Complete Direct Configuration Example

The following example initializes the SDK using direct configuration, creates a visitor context, runs an experiment, tracks a conversion, flushes queued events, and closes the SDK.

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()

7. Remote Initialization (SDK Key)

For production environments, initialize the SDK using your SDK Key.

Unlike Direct Configuration, this downloads the latest configuration from the Convert CDN over HTTPS.

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(result.variation_key)

core.flush()
core.close()

Never hard-code SDK Keys inside your application.

Instead, load them from:

  • environment variables
  • secret managers
  • deployment configuration

Remote Initialization Behavior

When initialized with:

SDKConfig(
    sdk_key="..."
)

the SDK:

  • downloads configuration over HTTPS
  • caches a configuration snapshot
  • performs all visitor evaluations locally

Only the initialization step requires network connectivity.

After configuration has been loaded:

  • experience evaluation
  • visitor bucketing
  • conversion tracking

all execute locally.

Initialization Errors

If configuration cannot be downloaded:

Core(...).initialize()

raises:

ConfigLoadError

Applications should handle this appropriately during startup.

Unlike run_experience() or track_conversion(), configuration loading failures are considered exceptional.

8. Context Manager Pattern

The SDK implements Python's context manager protocol.

This is the recommended approach for:

  • scripts
  • automation
  • command-line tools
  • short-lived processes

Example:

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(result.variation_key)

When execution leaves the block:

  • queued events are flushed
  • transport resources are closed automatically

Even if an exception occurs inside the block, cleanup still happens.

9. Backend and Front-End Implementation Considerations

The Python SDK performs experiment decisions on the backend.

Many Python applications use this decision to influence what the frontend ultimately receives.


Pattern A: Server-Side Rendering

For Django or Flask applications:

result = context.run_experience(
    "homepage-redesign"
)

if result is None:
    return render_template(
        "homepage_default.html"
    )

if result.variation_key == "treatment":
    return render_template(
        "homepage_treatment.html"
    )

return render_template(
    "homepage_default.html"
)

The browser immediately receives the correct HTML.

Advantages include:

  • no client-side flicker
  • consistent rendering
  • SEO-friendly pages
  • backend-controlled experimentation

Pattern B: Backend Decision + Frontend Behavior

Sometimes Python decides the variation while JavaScript handles interactive behavior.

Example:

result = context.run_experience(
    "homepage-redesign"
)

variation = (
    result.variation_key
    if result
    else "control"
)

return render_template(
    "homepage.html",
    variation=variation,
)

Then expose the assignment:

<script>
window.convertVariation = "";
</script>

Your frontend JavaScript can then react without performing a second experiment evaluation.

Pattern C: API-Driven Applications

For REST APIs:

return {
    "variation":
        result.variation_key
        if result
        else "control"
}

Native mobile applications or SPAs can then render the appropriate experience.

Avoid Duplicate Bucketing

Avoid evaluating the same experiment independently on:

  • backend
  • browser
  • mobile

unless they intentionally share:

  • visitor identifiers
  • configuration
  • assignment logic

Instead:

  • keep one source of truth
  • pass variation information downstream
  • reuse stable visitor IDs

This ensures visitors receive consistent experiences.

10. QA Checklist

Before deploying your implementation, verify:

  • Python 3.9 or newer
  • SDK installed successfully
  • SDK initializes without errors
  • core.is_ready returns True
  • Visitor contexts use stable visitor IDs
  • Visitor attributes are populated correctly
  • Experience keys match Convert
  • Goal keys match Convert
  • Revenue values are correct
  • Duplicate conversions behave as expected
  • core.flush() is called before shutdown
  • core.close() releases resources
  • SDK Key is loaded from environment variables (production)
  • Frontend receives the expected variation when applicable

11. Troubleshooting

run_experience() Always Returns None

Check:

  • experience key exists
  • visitor qualifies
  • configuration loaded successfully
  • SDK initialized correctly

Remember that None is a normal outcome—not an exception.

Conversion Not Recorded

Verify:

  • goal key exists
  • visitor context created successfully
  • conversion wasn't deduplicated
  • core.flush() executed

Unknown goal keys return:

ConversionStatus.GOAL_NOT_FOUND

rather than raising an exception.

Events Never Reach Convert

Remember:

core.flush()

must be called unless your application reaches the configured batching threshold or auto-flush interval.

For scripts and serverless environments, always flush before exit.

Configuration Download Fails

If using:

SDKConfig(
    sdk_key=...
)

verify:

  • SDK Key is valid
  • outbound HTTPS connectivity exists
  • CDN access is allowed

Initialization raises ConfigLoadError if configuration cannot be loaded.

Conclusion

The Convert Python SDK provides a fast and flexible way to run Full Stack experiments and feature flags from Python applications.

Use Direct Configuration for local development, testing, CI pipelines, and offline tooling where no network access is required. For production, initialize the SDK with your SDK Key to download the latest configuration from the Convert CDN.

Create one visitor context per visitor, evaluate experiences locally, track conversions synchronously, and remember to flush queued events before your application exits.

For frontend-facing implementations, keep one source of truth for experiment assignment and pass variation information to downstream applications rather than re-evaluating the same experiment multiple times.