Skip to content

Python

The edgeflags package provides async and sync clients for reading feature flags and configs from Python 3.10+. Both interfaces share the same API surface — choose async for asyncio applications or sync for scripts and traditional web frameworks.

SDKs are also available for JavaScript / TypeScript and React.

Installation

Terminal window
pip install edgeflags

Quick start

from edgeflags import AsyncEdgeFlags
ef = AsyncEdgeFlags(
token="ff_production_abc123",
base_url="https://edgeflags.net",
context={
"user_id": "u_42",
"plan": "premium",
"custom": {"country": "US"},
},
)
await ef.init()
dark_mode = ef.flag("dark_mode", False)
limits = ef.config("api_limits", {"requests_per_minute": 100})

After init() completes, all reads are synchronous from the local cache. The client polls for updates in the background.

Configuration

Pass these options to the constructor (both AsyncEdgeFlags and EdgeFlags accept the same parameters):

OptionTypeDefaultDescription
tokenstrrequiredBearer token for authentication
base_urlstrrequiredEdgeFlags API URL
contextEvaluationContextNoneUser context for targeting evaluation
polling_intervalfloat60.0Background poll interval in seconds
bootstrapdictNoneInitial flag/config values for instant reads before init()
debugboolFalseLog SDK activity to stderr

Evaluation context

The context dict is sent with every evaluation request and drives targeting rules:

from edgeflags import EvaluationContext
context: EvaluationContext = {
"user_id": "u_42",
"email": "alice@example.com",
"phone": "+1234567890",
"plan": "premium",
"segments": ["beta-testers"],
"environment": "production",
"custom": {"country": "US", "beta_enrolled": True},
}

EvaluationContext is a TypedDict — all fields except custom are optional.

Reading flags

Use flag() to read a flag value. Pass a default as the second argument:

# Returns the flag value, or None if not found
raw = ef.flag("dark_mode")
# Returns bool (default provides fallback)
dark_mode = ef.flag("dark_mode", False)
# String flags
banner = ef.flag("banner_text", "")
# Numeric flags
limit = ef.flag("request_limit", 1000)
# Dict flags
theme = ef.flag("theme_override", {"primary": "#000"})

Use all_flags() to get every cached flag as a dict[str, Any].

Reading configs

Use config() the same way:

providers = ef.config("payment_providers", {
"stripe_enabled": True,
"paypal_enabled": False,
})
all_configs = ef.all_configs()

Identifying users

Call identify() to update the evaluation context and immediately refresh all values:

await ef.identify({
"user_id": "u_99",
"email": "jane@example.com",
"plan": "enterprise",
"custom": {"beta_enrolled": True},
})
# Flags and configs now reflect the new user

Events

The client emits three events:

EventPayloadWhen
readyNoneinit() completes successfully
changeChangeEventFlag or config values change after a poll
errorExceptionNetwork or API error during polling
# Subscribe — returns an unsubscribe function
def on_change(event):
for change in event.flags:
print(f"Flag {change.key}: {change.previous} -> {change.current}")
off = ef.on("change", on_change)
ef.on("error", lambda err: print(f"EdgeFlags polling error: {err}"))
# Unsubscribe
off()

Bootstrap and offline fallback

Provide bootstrap values so flags are available immediately, before the first network request:

ef = EdgeFlags(
token="ff_production_abc123",
base_url="https://edgeflags.net",
bootstrap={
"flags": {"dark_mode": False, "new_checkout": True},
"configs": {"api_limits": {"requests_per_minute": 500}},
},
)
# Available immediately — no init() needed
dark_mode = ef.flag("dark_mode", False)
# init() will replace bootstrap values with live data
ef.init()

Testing

Use the mock client helpers to create a client that returns static values without making network requests:

from edgeflags import create_mock_client
mock = create_mock_client(
flags={"dark_mode": True, "new_checkout": False},
configs={"api_limits": {"requests_per_minute": 9999}},
)
# Works like a real client
mock.flag("dark_mode", False) # True
mock.config("api_limits") # {"requests_per_minute": 9999}

Cleanup

Stop the background polling and release resources when you’re done:

await ef.aclose()

AsyncEdgeFlags also works as an async context manager:

async with AsyncEdgeFlags(token=token, base_url=url) as ef:
await ef.init()
dark_mode = ef.flag("dark_mode", False)
# aclose() called automatically