How to Create and Manage Feature Flags Programmatically via the PostHog API
Use the PostHog REST API endpoints under /api/projects/{project_id}/feature_flags/ with Bearer token authentication to create, update, and delete feature flags programmatically, supporting both boolean and multivariate configurations.
The PostHog/posthog repository exposes a comprehensive REST API for feature flag management through the FeatureFlagViewSet class. Located in posthog/api/feature_flag.py, this viewset enables teams to automate feature rollouts, A/B testing, and targeting rules without manual UI interactions.
API Endpoints and Authentication
Available Endpoints
The FeatureFlagViewSet registered in posthog/api/__init__.py (lines 84-90) exposes the following endpoints under each project scope:
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/projects/{project_id}/feature_flags/ |
Create a new flag |
| GET | /api/projects/{project_id}/feature_flags/ |
List all flags |
| GET | /api/projects/{project_id}/feature_flags/{flag_id}/ |
Retrieve a specific flag |
| PATCH | /api/projects/{project_id}/feature_flags/{flag_id}/ |
Partial update |
| PUT | /api/projects/{project_id}/feature_flags/{flag_id}/ |
Full replacement |
| DELETE | /api/projects/{project_id}/feature_flags/{flag_id}/ |
Soft delete |
Authentication Methods
All requests require authentication via the backends defined in posthog/auth.py. Include one of the following in the Authorization header:
- Project API key:
Bearer <PROJECT_API_KEY> - Personal API key:
Bearer <PERSONAL_API_KEY> - Project Secret API key
DRF mixins automatically apply the appropriate authentication classes to each endpoint.
Request Schema and Validation
Core Fields
Creation payloads are validated by FeatureFlagCreateRequestSchemaSerializer (lines 604-623 in posthog/api/feature_flag.py), while updates use FeatureFlagPartialUpdateRequestSchemaSerializer (lines 625-644). Both serializers accept:
key: Unique identifier matching regex^[a-zA-Z0-9_-]+$name: Human-readable descriptionfilters: Targeting configuration objectactive: Boolean flag statustags: Array of organization-wide tagsevaluation_contexts: Runtime contexts (e.g.,"production","staging")
Responses are generated by FeatureFlagSerializer (lines 646-674), returning computed fields like status, evaluation_contexts, and linked resources.
Filter Configuration Format
The filters object follows FeatureFlagFiltersSchemaSerializer from posthog/api/documentation.py:
{
"groups": [
{
"properties": [
{
"key": "email",
"type": "person",
"operator": "icontains",
"value": "@example.com"
}
],
"rollout_percentage": 100
}
]
}
Groups function as OR clauses; properties within groups are AND-combined. Supported operators include icontains, gt, is_date_before, and flag_evaluates_to (defined in FEATURE_FLAG_SUPPORTED_OPERATORS, lines 534-588).
Creating Feature Flags
Boolean Flags
Create a simple on/off flag with user targeting:
curl -X POST "https://app.posthog.com/api/projects/123/feature_flags/" \
-H "Authorization: Bearer <PROJECT_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"key": "new-feature",
"name": "New Feature rollout",
"filters": {
"groups": [
{
"properties": [
{ "key": "email", "type": "person", "operator": "icontains", "value": "@example.com" }
],
"rollout_percentage": 100
}
]
},
"active": true,
"tags": ["beta"]
}'
Multivariate Flags
For A/B testing, include a multivariate block with variants:
curl -X POST "https://app.posthog.com/api/projects/123/feature_flags/" \
-H "Authorization: Bearer <PROJECT_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"key": "ui-experiment",
"name": "UI Experiment",
"filters": {
"multivariate": {
"variants": [
{ "key": "control", "rollout_percentage": 50 },
{ "key": "variant_a", "rollout_percentage": 50 }
]
},
"groups": [
{
"properties": [],
"rollout_percentage": 100
}
]
},
"active": true
}'
Updating and Retrieving Flags
Partial Updates
Use PATCH to modify specific fields without replacing the entire resource:
curl -X PATCH "https://app.posthog.com/api/projects/123/feature_flags/42/" \
-H "Authorization: Bearer <PROJECT_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"tags": ["beta", "released"]
}'
Listing and Fetching
Retrieve a specific flag using Python:
import requests
API_KEY = "<PROJECT_API_KEY>"
PROJECT_ID = 123
FLAG_ID = 42
url = f"https://app.posthog.com/api/projects/{PROJECT_ID}/feature_flags/{FLAG_ID}/"
headers = {"Authorization": f"Bearer {API_KEY}"}
resp = requests.get(url, headers=headers)
resp.raise_for_status()
flag = resp.json()
print(flag["key"], flag["active"], flag["filters"])
List all flags with pagination:
resp = requests.get(
f"https://app.posthog.com/api/projects/{PROJECT_ID}/feature_flags/",
headers={"Authorization": f"Bearer {API_KEY}"},
params={"limit": 100}
)
flags = resp.json()["results"]
for f in flags:
print(f["key"], f["active"])
Limits and Validation Constraints
The API enforces several guardrails defined in posthog/api/feature_flag.py:
- Team flag limit:
check_flag_limits_for_team(lines 63-78) enforces maximum flags per team via the Django settingMAX_FEATURE_FLAGS_PER_TEAM(configurable inposthog/settings/feature_flags.py). - Key uniqueness: Validated by
validate_key; duplicate keys per project return 400 Bad Request. - Payload size:
calculate_filter_size_bytes(lines 49-61) limits the JSON size of filter configurations.
Violation of these constraints returns HTTP 400 with detailed error messages.
Summary
- The FeatureFlagViewSet in
posthog/api/feature_flag.pyhandles all CRUD operations for feature flags under/api/projects/{project_id}/feature_flags/. - Authentication requires Bearer tokens using Project API keys, Personal API keys, or Project Secret API keys.
- Boolean flags target users via
filters.groupswith property conditions and rollout percentages. - Multivariate flags require a
multivariate.variantsarray where rollout percentages must sum to 100%. - Team limits are enforced by
check_flag_limits_for_teamand controlled byMAX_FEATURE_FLAGS_PER_TEAM. - Use PATCH for partial updates and PUT for complete flag replacement.
Frequently Asked Questions
What authentication methods does the PostHog Feature Flags API support?
The API supports Project API keys, Personal API keys, and Project Secret API keys, all passed as Bearer tokens in the Authorization header. The authentication backends are implemented in posthog/auth.py and integrated automatically through Django REST Framework mixins.
How do I implement multivariate feature flags via the API?
Include a multivariate object within the filters payload containing a variants array. Each variant must specify a key and rollout_percentage, with all percentages summing to exactly 100. The request is validated by FeatureFlagCreateRequestSchemaSerializer in posthog/api/feature_flag.py.
What are the limits on feature flag creation?
PostHog enforces a team-wide limit on the total number of feature flags, controlled by the MAX_FEATURE_FLAGS_PER_TEAM setting in posthog/settings/feature_flags.py. Additionally, the calculate_filter_size_bytes function limits the JSON payload size of filter configurations. Both constraints return HTTP 400 errors when exceeded.
What is the difference between PATCH and PUT when updating flags?
PATCH requests use FeatureFlagPartialUpdateRequestSchemaSerializer and update only the provided fields, making them ideal for adding tags or toggling the active status. PUT requests replace the entire flag resource, requiring all required fields in the payload. Both methods target /api/projects/{project_id}/feature_flags/{flag_id}/.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →