---
title: Python Feature Flags
description: Set up Datadog Feature Flags for Python applications.
breadcrumbs: Docs > Feature Flags > Server-Side Feature Flags > Python Feature Flags
---

> For the complete documentation index, see [llms.txt](https://docs.datadoghq.com/llms.txt).

# Python Feature Flags

{% callout %}
# Important note for users on the following Datadog sites: app.ddog-gov.com, us2.ddog-gov.com

{% alert level="danger" %}
This product is not supported for your selected [Datadog site](https://docs.datadoghq.com/getting_started/site.md). ({% placeholder "user-datadog-site-name" /%}).
{% /alert %}

{% /callout %}

## Overview{% #overview %}

This page describes how to instrument your Python application with the Datadog Feature Flags SDK. The Python SDK integrates with [OpenFeature](https://openfeature.dev/), an open standard for feature flag management. Starting in `ddtrace` 4.14.0, it loads flag configuration directly from the Datadog-managed CDN by default.

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

{% alert level="warning" %}
Python agentless delivery changes only the configuration source. Without a supported Datadog Agent or serverless telemetry path, the SDK does not export evaluation metrics or exposure events.
{% /alert %}

## Prerequisites{% #prerequisites %}

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

- **Datadog Python SDK** `ddtrace` version 4.14.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)
- A Datadog [API key](https://docs.datadoghq.com/account_management/api-app-keys.md#api-keys)
- Your Datadog site

Set the following environment variables:

```bash
# Required: Agentless configuration delivery
export DD_API_KEY=<YOUR_API_KEY>
export DD_SITE=<code class="js-region-param region-param" data-region-param="dd_site"></code>
export DD_ENV=<YOUR_ENVIRONMENT>

# Optional: Enable flag evaluation metrics
export DD_METRICS_OTEL_ENABLED=true

# Recommended: Service identification
export DD_SERVICE=<YOUR_SERVICE_NAME>
```

No Feature Flags enablement or source setting is required. Register the provider as shown in Initialize the SDK to begin polling. Installing or initializing `ddtrace` alone does not create Feature Flags CDN traffic.

To configure `feature_flag.evaluations`, including the required tracer version and Agent OTLP setup, see [Set Up Server-Side Flag Evaluation Metrics](https://docs.datadoghq.com/feature_flags/guide/server_flag_evaluation_metrics.md). For more information on available graphing, see [Feature Flag Graphs](https://docs.datadoghq.com/feature_flags/concepts/flag_graphs.md).

## Installation{% #installation %}

Install the Datadog Python SDK and OpenFeature SDK:

```bash
pip install ddtrace openfeature-sdk
```

Or add them to your `requirements.txt`:

In the `requirements.txt` file:

```text
ddtrace>=4.14.0
openfeature-sdk>=0.5.0
```

If you enable flag evaluation metrics, you must also install the OpenTelemetry SDK and OTLP exporter:

```bash
pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-grpc
```

Or add them to your `requirements.txt`:

In the `requirements.txt` file:

```text
opentelemetry-sdk>=1.41.0
opentelemetry-exporter-otlp-proto-grpc>=1.41.0
```

## Initialize the SDK{% #initialize-the-sdk %}

Register the Datadog OpenFeature provider with the OpenFeature API. The provider starts the selected configuration source and waits up to 10 seconds for its first configuration.

```python
from openfeature import api
from ddtrace.openfeature import DataDogProvider

# 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{% #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:

{% alert level="warning" %}
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.
{% /alert %}

```python
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{% #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{% #boolean-flags %}

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

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

if enabled:
    show_new_checkout()
else:
    show_legacy_checkout()
```

### String flags{% #string-flags %}

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

```python
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{% #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:

```python
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{% #object-flags %}

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

```python
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{% #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:

```python
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{% #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:

```python
# 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{% #waiting-for-provider-initialization %}

Provider registration waits up to 10 seconds for the selected source to deliver its first configuration. If configuration arrives, the provider emits `PROVIDER_READY`. If the wait times out, registration completes with the provider in an error state, and evaluations return caller-provided default values until configuration arrives. Use an event handler to wait for a later ready event:

```python
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 the provider to be ready if registration timed out
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()
```

{% alert level="info" %}
Provider event handlers require OpenFeature SDK 0.7.0 or later. Most applications can use the default 10-second initialization timeout and handle caller-provided default values if configuration is unavailable.
{% /alert %}

Set `DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS` to a positive number of milliseconds to change the initialization timeout.

## Advanced configuration{% #advanced-configuration %}

Use [Server SDK Configuration Sources](https://docs.datadoghq.com/feature_flags/concepts/configuration_sources.md) as the canonical reference for source selection and operational settings:

- [Configure agentless delivery](https://docs.datadoghq.com/feature_flags/concepts/configuration_sources.md#configure-agentless-delivery), including polling, request timeout, and endpoint settings
- [Use a custom agentless endpoint](https://docs.datadoghq.com/feature_flags/concepts/configuration_sources.md#use-a-custom-agentless-endpoint) for advanced testing, local development, or an operator-managed proxy
- [Use Agent Remote Configuration](https://docs.datadoghq.com/feature_flags/concepts/configuration_sources.md#use-agent-remote-configuration) to retain Agent-managed delivery
- [Migrate an existing Remote Configuration setup](https://docs.datadoghq.com/feature_flags/concepts/configuration_sources.md#migrate-an-existing-remote-configuration-setup) and remove the deprecated `DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED` setting

Agentless mode changes only flag configuration. It does not configure or enable `feature_flag.evaluations`, exposure logging, or experimentation use cases. These features require a supported Datadog Agent or serverless telemetry path.

## Cleanup{% #cleanup %}

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

```python
api.shutdown()
```

## Testing{% #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.

In the `test_flags.py` file:

```python
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{% #troubleshooting %}

### Agentless configuration not working{% #agentless-configuration-not-working %}

Verify the following:

- `ddtrace` is version 4.14.0 or later.
- `DD_FEATURE_FLAGS_ENABLED` is unset or set to `true`.
- `DD_FEATURE_FLAGS_CONFIGURATION_SOURCE` is unset or set to `agentless`.
- `DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED` is unset. Setting it to `true` selects Agent Remote Configuration during the migration window when no explicit source is set.
- Application code registers `DataDogProvider` with the OpenFeature API.
- `DD_API_KEY`, `DD_SITE`, and `DD_ENV` are configured in the application process.
- The application can make outbound HTTPS requests to Datadog.

Set `DD_TRACE_DEBUG=true` and check for authentication, timeout, or malformed-payload messages from the Feature Flags agentless endpoint.

### Agent Remote Configuration not working{% #agent-remote-configuration-not-working %}

Verify the following:

- `DD_FEATURE_FLAGS_CONFIGURATION_SOURCE=remote_config` is set. During the migration window, `DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true` also selects Remote Configuration when no explicit source is set.
- Datadog Agent is version 7.55 or later.
- [Remote Configuration](https://docs.datadoghq.com/agent/remote_config.md) is enabled on the Agent.
- The Agent has a valid API key for the target organization.
- `DD_SERVICE` and `DD_ENV` are configured in the application process.
- The SDK can communicate with the Agent.

## Further reading{% #further-reading %}

Additional helpful documentation, links, and articles:

- [Server-Side Feature Flags](https://docs.datadoghq.com/feature_flags/server.md)
- [Python Tracing](https://docs.datadoghq.com/tracing/trace_collection/dd_libraries/python.md)
- [Set Up Server-Side Flag Evaluation Metrics](https://docs.datadoghq.com/feature_flags/guide/server_flag_evaluation_metrics.md)
- [Set Up APM Trace Enrichment for Feature Flags](https://docs.datadoghq.com/feature_flags/guide/apm_trace_enrichment.md)
- [Feature Flag Graphs](https://docs.datadoghq.com/feature_flags/concepts/flag_graphs.md)
- [Server SDK Configuration Sources](https://docs.datadoghq.com/feature_flags/concepts/configuration_sources.md)
