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 description
  • filters: Targeting configuration object
  • active: Boolean flag status
  • tags: Array of organization-wide tags
  • evaluation_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 setting MAX_FEATURE_FLAGS_PER_TEAM (configurable in posthog/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.py handles 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.groups with property conditions and rollout percentages.
  • Multivariate flags require a multivariate.variants array where rollout percentages must sum to 100%.
  • Team limits are enforced by check_flag_limits_for_team and controlled by MAX_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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →