Skip to content

Python SDK

Python SDK for feature flags and secrets with SSE streaming.

Terminal window
pip install getsoup

Requires Python 3.9+.

from getsoup import SoupClient
client = SoupClient(
api_key="sk_project_xxx",
project_slug="my-app",
environment_slug="production",
)
# Feature flags
client.flags.identify(key="user-123", attributes={"plan": "pro"})
if client.flags.bool_flag("new-checkout", default=False):
show_new_checkout()
# Secrets
db_url = client.secrets.get("DATABASE_URL")
client.close()

Boolean flags:

enabled = client.flags.bool_flag("dark-mode", default=False)

Typed flag helpers:

limit = client.flags.number_flag("rate-limit", default=100)
cta = client.flags.string_flag("cta-text", default="Sign up")
config = client.flags.json_flag("ui-config", default={})

Evaluation context:

client.flags.identify(
key="user-123",
attributes={
"plan": "enterprise",
"beta_tester": True,
"signup_date": "2025-01-15",
}
)

Context persists for all subsequent flag evaluations.

Get secret:

api_key = client.secrets.get("API_KEY")

Returns None if not found.

Inject into environment:

client.secrets.inject_env()
import os
db_url = os.getenv("DATABASE_URL")

Loads all secrets into os.environ.

Fetch content entries:

# Get all entries of a type
posts = client.cms.entries("blog-post", status="published")
# Get single entry
post = client.cms.entry("blog-post", "getting-started")
print(post["data"]["title"])

Query with filters:

posts = client.cms.entries(
"blog-post",
status="published",
page=1,
limit=10
)

Content types:

# List all content types
types = client.cms.types()
# Get single type
blog_type = client.cms.type("blog-post")
print(blog_type["fields"])

Evaluate segment:

matched = client.segments.evaluate(
"enterprise-users",
context={"user_id": "user-123", "plan": "enterprise"}
)
if matched:
show_enterprise_features()

List segments:

segments = client.segments.list()
for segment in segments:
print(segment["key"], segment["name"])

List cron jobs:

jobs = client.cron.list()
for job in jobs:
print(f"{job['name']}: {job['schedule']}")

Get job runs:

runs = client.cron.runs("daily-sync")
for run in runs:
print(f"Status: {run['status']}, Duration: {run['duration_ms']}ms")

Trigger manually:

run = client.cron.trigger("daily-sync")
print(f"Run ID: {run['id']}")

List workflows:

workflows = client.workflows.list()
for wf in workflows:
print(f"{wf['name']}: {wf['trigger_type']}")

Trigger workflow:

run = client.workflows.trigger(
"user-onboarding",
payload={"email": "[email protected]", "name": "Alice"}
)
print(f"Run ID: {run['id']}")

Get workflow runs:

runs = client.workflows.runs("user-onboarding")
for run in runs:
print(f"Status: {run['status']}, Steps: {len(run['steps'])}")

SSE streaming is enabled by default. Flags update automatically when changed in dashboard.

Listen for updates:

def on_flags_updated(keys):
print(f"Flags changed: {keys}")
client.flags.on("flags_updated", on_flags_updated)

Disable streaming:

client = SoupClient(
api_key="sk_project_xxx",
project_slug="my-app",
environment_slug="production",
streaming=False,
)

Explicit config:

client = SoupClient(
api_key="sk_project_xxx",
project_slug="my-app",
environment_slug="production",
base_url="https://api.getsoup.dev", # default
streaming=True, # default
)

Auto-resolve config:

client = SoupClient.from_config()

Resolution priority:

  1. Explicit arguments
  2. Environment variables (SOUP_API_KEY, SOUP_PROJECT, SOUP_ENVIRONMENT)
  3. Directory .soup/config
  4. soup.yaml in repo root
  5. ~/.soup/config (from CLI login)
with SoupClient(api_key="sk_project_xxx", project_slug="my-app", environment_slug="production") as client:
client.flags.identify(key="user-123")
enabled = client.flags.bool_flag("feature", default=False)

Automatically calls close() on exit.

Generate project API keys in dashboard:

  • Settings → API Keys → Create Key
  • Format: sk_project_xxx

For self-hosted, use master API key from soup-engine config.