For AI agents: A markdown version of this page is available at https://docs.datadoghq.com/feature_flags/server/python.md. A documentation index is available at /llms.txt.
This product is not supported for your selected Datadog site. ().

Overview

This page describes how to instrument your Python application with the Datadog Feature Flags SDK. The Python SDK integrates with OpenFeature, an open standard for feature flag management, and receives flag updates through Remote Configuration in the Datadog Python tracer (ddtrace).

This guide explains how to install and enable the SDK, create an OpenFeature client, and evaluate feature flags in your application.

Prerequisites

Before setting up the Python Feature Flags SDK, ensure you have:

  • Datadog Agent version 7.55 or later with Remote Configuration enabled
  • Datadog API key configured on the Agent
  • Datadog Python SDK ddtrace version 3.19.0 or later
  • OpenFeature Python SDK openfeature-sdk: version 0.5.0 or later (version 0.7.0 or later required if you use provider event handlers to wait for initialization)

Set the following environment variables:

# Required: Enable the feature flags provider
export DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true

# Optional: Enable flag evaluation metrics
export DD_METRICS_OTEL_ENABLED=true

# Required: Service identification
export DD_SERVICE=<YOUR_SERVICE_NAME>
export DD_ENV=<YOUR_ENVIRONMENT>
The EXPERIMENTAL_ prefix is retained for backwards compatibility; the provider itself is stable.

Installation

Install the Datadog Python SDK and OpenFeature SDK:

pip install ddtrace openfeature-sdk

Or add them to your requirements.txt:

requirements.txt

ddtrace>=3.19.0
openfeature-sdk>=0.5.0

Initialize the SDK

Register the Datadog OpenFeature provider with the OpenFeature API. The provider connects to the Datadog Python tracer’s Remote Configuration system to receive flag configurations.

from ddtrace import tracer
from openfeature import api
from ddtrace.openfeature import DataDogProvider

# Initialize the tracer (required for Remote Configuration)
tracer.configure()

# Create and register the Datadog provider
provider = DataDogProvider()
api.set_provider(provider)

# Create an OpenFeature client
client = api.get_client()

# Your application code here

Set the evaluation context

Define an evaluation context that identifies the user or entity for flag targeting. The evaluation context includes attributes used to determine which flag variations should be returned:

Datadog Feature Flags requires evaluation context attributes to be flat primitive values: strings, numbers, and Booleans. Do not pass nested objects or arrays; they are not supported and can cause exposure data to be dropped.
from openfeature.evaluation_context import EvaluationContext

eval_ctx = EvaluationContext(
    targeting_key="user-123",  # Targeting key (typically user ID)
    attributes={
        "email": "user@example.com",
        "country": "US",
        "tier": "premium",
        "age": 25
    }
)

The targeting key is used for consistent traffic distribution (percentage rollouts). Additional attributes enable targeting rules, such as “enable for users in the US” or “enable for premium tier users” in the example above.

Evaluate flags

After setting up the provider and creating a client, you can evaluate flags throughout your application. Flag evaluation is local and fast—the SDK uses locally cached configuration data, so no network requests occur during evaluation.

Each flag is identified by a key (a unique string) and can be evaluated with a typed method that returns a value of the expected type. If the flag doesn’t exist or cannot be evaluated, the SDK returns the provided default value.

Boolean flags

Use get_boolean_value for flags that represent on/off or true/false conditions:

enabled = client.get_boolean_value("new-checkout-flow", False, eval_ctx)

if enabled:
    show_new_checkout()
else:
    show_legacy_checkout()

String flags

Use get_string_value for flags that select between multiple variants or configuration strings:

theme = client.get_string_value("ui-theme", "light", eval_ctx)

if theme == "dark":
    set_dark_theme()
elif theme == "light":
    set_light_theme()
else:
    set_light_theme()

Numeric flags

For numeric flags, use get_integer_value or get_float_value. These are appropriate when a feature depends on a numeric parameter such as a limit, percentage, or multiplier:

max_items = client.get_integer_value("cart-max-items", 20, eval_ctx)

discount_rate = client.get_float_value("discount-rate", 0.0, eval_ctx)

Object flags

For structured data, use get_object_value. This returns a dictionary with complex configuration:

config = client.get_object_value("feature-config", {
    "maxRetries": 3,
    "timeout": 30
}, eval_ctx)

max_retries = config.get("maxRetries", 3)
timeout = config.get("timeout", 30)

Flag evaluation details

When you need more than just the flag value, use the *_details methods. These return both the evaluated value and metadata explaining the evaluation:

details = client.get_boolean_details("new-feature", False, eval_ctx)

print(f"Value: {details.value}")
print(f"Variant: {details.variant}")
print(f"Reason: {details.reason}")
print(f"Error Code: {details.error_code}")
print(f"Error Message: {details.error_message}")

Flag details help you debug evaluation behavior and understand why a user received a given value.

Evaluation without context

You can evaluate flags without providing an evaluation context. This is useful for global flags that don’t require user-specific targeting:

# Global feature flag - no context needed
maintenance_mode = client.get_boolean_value("maintenance-mode", False)

if maintenance_mode:
    return "Service temporarily unavailable"

Waiting for provider initialization

By default, the provider initializes asynchronously and flag evaluations return default values until the first Remote Configuration payload is received. If your application requires flags to be ready before handling requests, you can wait for the provider to initialize using event handlers:

import threading
from openfeature import api
from openfeature.event import ProviderEvent
from ddtrace.openfeature import DataDogProvider

# Create an event to wait for readiness
ready_event = threading.Event()

def on_ready(event_details):
    ready_event.set()

# Register event handler
api.add_handler(ProviderEvent.PROVIDER_READY, on_ready)

# Set provider
provider = DataDogProvider()
api.set_provider(provider)

# Wait for provider to be ready (with optional timeout)
if ready_event.wait(timeout=30):
    print("Provider is ready")
else:
    print("Provider initialization timed out")

# Create client and evaluate flags
client = api.get_client()
Waiting for provider initialization requires OpenFeature SDK 0.7.0 or later. Most applications don't need to wait for initialization, as flag evaluations work immediately with default values.

Cleanup

When your application exits, shut down the OpenFeature API to clean up resources:

api.shutdown()

Testing

You can test against a dedicated Datadog test environment with the real Datadog provider, or swap it for OpenFeature’s InMemoryProvider to control flag values directly in test code. This section shows the in-memory approach, which keeps tests hermetic and offline. InMemoryProvider is bundled with openfeature-sdk, so no additional dependency is required.

The OpenFeature API is a global singleton (openfeature.api.set_provider mutates module-level state). Use a function-scoped pytest fixture and call api.shutdown() in teardown so tests do not leak flag state into each other.

test_flags.py

import pytest
from openfeature import api
from openfeature.evaluation_context import EvaluationContext
from openfeature.provider.in_memory_provider import InMemoryProvider, InMemoryFlag


@pytest.fixture
def client():
    flags = {
        "new-checkout-flow": InMemoryFlag(
            default_variant="off",
            variants={"on": True, "off": False},
        ),
        "ui-theme": InMemoryFlag(
            default_variant="light",
            variants={"light": "light", "dark": "dark"},
        ),
    }
    api.set_provider(InMemoryProvider(flags))
    yield api.get_client()
    api.shutdown()


def test_boolean_flag_returns_default_variant(client):
    assert client.get_boolean_value("new-checkout-flow", True) is False


def test_string_flag_with_context(client):
    ctx = EvaluationContext(targeting_key="user-123")
    assert client.get_string_value("ui-theme", "dark", ctx) == "light"


def test_missing_flag_returns_default(client):
    assert client.get_boolean_value("does-not-exist", True) is True

InMemoryFlag takes default_variant (a string variant name) and variants (a dict mapping variant names to typed values). Passing a value as default_variant instead of a variant name is a common mistake. For targeting logic, pass a context_evaluator callback that receives the flag and an EvaluationContext and returns a FlagResolutionDetails object carrying the chosen variant.

Troubleshooting

Provider not enabled

If you receive warnings about the provider not being enabled, ensure DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true is set in your environment:

export DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true

Remote Configuration not working

Verify the following to ensure that Remote Configuration is working:

  • Datadog Agent is version 7.55 or later
  • Remote Configuration is enabled on the Agent
  • DD_SERVICE and DD_ENV environment variables are set
  • The SDK can communicate with the Agent

Further reading

Additional helpful documentation, links, and articles: