From b9f320808f44832fc3b4cdd6bcf0e0aad194b76d Mon Sep 17 00:00:00 2001
From: Hugues Pouillot
Date: Mon, 15 Jun 2026 11:56:27 +0200
Subject: [PATCH 1/8] feat(exception-capture): add client-side token bucket
rate limiting (#662)
* feat(exception-capture): add client-side token bucket rate limiting
Port the posthog-js BucketedRateLimiter (packages/core/src/utils/
bucketed-rate-limiter.ts) and apply it to exception autocapture with
the same settings as the browser and Node SDKs: one bucket per
exception type, bucket size 10, refilling 1 token per 10 seconds.
Resolves the long-standing TODO in exception_capture.py.
Co-Authored-By: Claude Fable 5
* feat(exception-capture): make rate limiting configurable
Expose exception_autocapture_bucket_size, exception_autocapture_refill_rate
and exception_autocapture_refill_interval_seconds on Client and the
module-level API, passed through to ExceptionCapture's rate limiter.
Co-Authored-By: Claude Fable 5
* feat(exception-capture): raise default rate limits for server workloads
Bucket size 50 refilling 10 tokens per 10 seconds (was 10/1/10, the
browser SDK defaults) since one server process aggregates exceptions
across many users' requests. Defaults now live on ExceptionCapture and
are referenced by Client and the module-level API.
Co-Authored-By: Claude Fable 5
* feat(exception-capture): make rate limiting opt-in
Add enable_exception_autocapture_rate_limiting (default False) on
Client, the module-level API and ExceptionCapture. The limiter is only
constructed when enabled, so default behavior is unchanged from
released versions.
Co-Authored-By: Claude Fable 5
* fix(exception-capture): key rate limiter on root cause of chained exceptions
PostHog groups exceptions by $exception_list[0].type, which is the root
cause (exceptions_from_error_tuple reverses the walked chain), not the
wrapping exception. Walk the chain so e.g. `RuntimeError from
ZeroDivisionError` is keyed on ZeroDivisionError, matching server-side
grouping and posthog-js.
Also move the ExceptionCapture integration tests out of
test_bucketed_rate_limiter.py into test_exception_capture.py so each
file covers a single unit.
Co-Authored-By: Claude Fable 5
---------
Co-authored-by: Claude Fable 5
---
.../exception-autocapture-rate-limiting.md | 5 +
posthog/__init__.py | 20 ++
posthog/bucketed_rate_limiter.py | 119 ++++++++++
posthog/client.py | 30 ++-
posthog/exception_capture.py | 62 ++++-
posthog/test/test_bucketed_rate_limiter.py | 217 ++++++++++++++++++
posthog/test/test_exception_capture.py | 122 ++++++++++
7 files changed, 571 insertions(+), 4 deletions(-)
create mode 100644 .sampo/changesets/exception-autocapture-rate-limiting.md
create mode 100644 posthog/bucketed_rate_limiter.py
create mode 100644 posthog/test/test_bucketed_rate_limiter.py
diff --git a/.sampo/changesets/exception-autocapture-rate-limiting.md b/.sampo/changesets/exception-autocapture-rate-limiting.md
new file mode 100644
index 00000000..ae6743e7
--- /dev/null
+++ b/.sampo/changesets/exception-autocapture-rate-limiting.md
@@ -0,0 +1,5 @@
+---
+pypi/posthog: minor
+---
+
+Add opt-in client-side rate limiting for exception autocapture, using the same token bucket algorithm as the posthog-js and posthog-node SDKs: a bucket per exception type allows a burst of captures, then refills over time. Rate-limited exceptions are skipped before they reach the ingestion queue. Disabled by default; enable with the new `enable_exception_autocapture_rate_limiting` client option and tune via `exception_autocapture_bucket_size` (default 50), `exception_autocapture_refill_rate` (default 10), and `exception_autocapture_refill_interval_seconds` (default 10).
diff --git a/posthog/__init__.py b/posthog/__init__.py
index 892a68c6..a8657b71 100644
--- a/posthog/__init__.py
+++ b/posthog/__init__.py
@@ -5,6 +5,7 @@
from posthog.args import ExceptionArg, OptionalCaptureArgs, OptionalSetArgs
from posthog.client import Client
+from posthog.exception_capture import ExceptionCapture
from posthog.contexts import (
identify_context as inner_identify_context,
)
@@ -304,6 +305,15 @@ def get_tags() -> Dict[str, Any]:
code variables.
in_app_modules: Module/package prefixes treated as in-app frames in captured
exceptions.
+ enable_exception_autocapture_rate_limiting: Rate limit autocaptured
+ exceptions client-side with a token bucket per exception type. Disabled
+ by default.
+ exception_autocapture_bucket_size: Maximum burst of autocaptured exceptions
+ allowed per exception type (token bucket size, clamped to 0-100).
+ exception_autocapture_refill_rate: Tokens restored per refill interval for
+ each exception type's bucket.
+ exception_autocapture_refill_interval_seconds: Seconds between token refills
+ for autocaptured exception rate limiting.
"""
api_key = None # type: Optional[str]
host = None # type: Optional[str]
@@ -337,6 +347,12 @@ def get_tags() -> Dict[str, Any]:
code_variables_mask_patterns = DEFAULT_CODE_VARIABLES_MASK_PATTERNS
code_variables_ignore_patterns = DEFAULT_CODE_VARIABLES_IGNORE_PATTERNS
in_app_modules = None # type: Optional[list[str]]
+enable_exception_autocapture_rate_limiting = False # type: bool
+exception_autocapture_bucket_size = ExceptionCapture.DEFAULT_BUCKET_SIZE # type: int
+exception_autocapture_refill_rate = ExceptionCapture.DEFAULT_REFILL_RATE # type: int
+exception_autocapture_refill_interval_seconds = (
+ ExceptionCapture.DEFAULT_REFILL_INTERVAL_SECONDS
+) # type: float
# NOTE - this and following functions take unpacked kwargs because we needed to make
@@ -1103,6 +1119,10 @@ def setup() -> Client:
code_variables_mask_patterns=code_variables_mask_patterns,
code_variables_ignore_patterns=code_variables_ignore_patterns,
in_app_modules=in_app_modules,
+ enable_exception_autocapture_rate_limiting=enable_exception_autocapture_rate_limiting,
+ exception_autocapture_bucket_size=exception_autocapture_bucket_size,
+ exception_autocapture_refill_rate=exception_autocapture_refill_rate,
+ exception_autocapture_refill_interval_seconds=exception_autocapture_refill_interval_seconds,
)
# Always set in case user changes it. Preserve Client's auto-disabled state
diff --git a/posthog/bucketed_rate_limiter.py b/posthog/bucketed_rate_limiter.py
new file mode 100644
index 00000000..7cf24be9
--- /dev/null
+++ b/posthog/bucketed_rate_limiter.py
@@ -0,0 +1,119 @@
+# Python port of the posthog-js BucketedRateLimiter:
+# https://github.com/PostHog/posthog-js/blob/main/packages/core/src/utils/bucketed-rate-limiter.ts
+# Kept behaviorally identical so rate limiting is consistent across SDKs.
+
+import logging
+import threading
+import time
+from typing import Callable, Dict, Hashable, Optional, Union
+
+ONE_DAY_IN_SECONDS = 86400.0
+
+log = logging.getLogger("posthog")
+
+Number = Union[int, float]
+
+
+def _clamp_to_range(value, min_value: Number, max_value: Number, label: str) -> Number:
+ if isinstance(value, bool) or not isinstance(value, (int, float)):
+ log.warning(f"{label} must be a number. Using max value {max_value}.")
+ return max_value
+ if value > max_value:
+ log.warning(f"{label} cannot be greater than {max_value}. Using {max_value}.")
+ return max_value
+ if value < min_value:
+ log.warning(f"{label} cannot be less than {min_value}. Using {min_value}.")
+ return min_value
+ return value
+
+
+class _Bucket:
+ __slots__ = ("tokens", "last_access")
+
+ def __init__(self, tokens: Number, last_access: float):
+ self.tokens = tokens
+ self.last_access = last_access
+
+
+class BucketedRateLimiter:
+ """Token bucket rate limiter that tracks a separate bucket per key.
+
+ Each key starts with a full bucket of ``bucket_size`` tokens and every
+ call to :meth:`consume_rate_limit` consumes one token. ``refill_rate``
+ tokens are restored per elapsed ``refill_interval_seconds`` (whole
+ intervals only, fractional elapsed time is carried over), capped at
+ ``bucket_size``.
+
+ The call that empties a bucket is itself reported as rate limited — a
+ burst over a fresh bucket lets ``bucket_size - 1`` events through before
+ limiting kicks in — and ``on_bucket_rate_limited`` fires once each time a
+ bucket is drained.
+
+ Thread-safe. ``clock`` must return seconds and is injectable for tests.
+ """
+
+ def __init__(
+ self,
+ bucket_size: Number,
+ refill_rate: Number,
+ refill_interval_seconds: Number,
+ on_bucket_rate_limited: Optional[Callable[[Hashable], None]] = None,
+ clock: Callable[[], float] = time.monotonic,
+ ):
+ self._bucket_size = _clamp_to_range(bucket_size, 0, 100, "bucket_size")
+ self._refill_rate = _clamp_to_range(
+ refill_rate, 0, self._bucket_size, "refill_rate"
+ )
+ self._refill_interval = _clamp_to_range(
+ refill_interval_seconds, 0, ONE_DAY_IN_SECONDS, "refill_interval_seconds"
+ )
+ self._on_bucket_rate_limited = on_bucket_rate_limited
+ self._clock = clock
+ self._buckets: Dict[Hashable, _Bucket] = {}
+ self._lock = threading.Lock()
+
+ def _apply_refill(self, bucket: _Bucket, now: float) -> None:
+ if self._refill_interval <= 0:
+ bucket.tokens = self._bucket_size
+ bucket.last_access = now
+ return
+
+ elapsed = now - bucket.last_access
+ refill_intervals = int(elapsed // self._refill_interval)
+
+ if refill_intervals > 0:
+ tokens_to_add = refill_intervals * self._refill_rate
+ bucket.tokens = min(bucket.tokens + tokens_to_add, self._bucket_size)
+ # advance by whole intervals so fractional elapsed time still
+ # counts towards the next refill
+ bucket.last_access += refill_intervals * self._refill_interval
+
+ def consume_rate_limit(self, key: Hashable) -> bool:
+ """Consume one token for ``key``. Returns True if rate limited."""
+ callback = None
+
+ with self._lock:
+ now = self._clock()
+ bucket = self._buckets.get(key)
+
+ if bucket is None:
+ bucket = _Bucket(tokens=self._bucket_size, last_access=now)
+ self._buckets[key] = bucket
+ else:
+ self._apply_refill(bucket, now)
+
+ if bucket.tokens <= 0:
+ return True
+
+ bucket.tokens -= 1
+ rate_limited = bucket.tokens <= 0
+ if rate_limited:
+ callback = self._on_bucket_rate_limited
+
+ if callback is not None:
+ callback(key)
+ return rate_limited
+
+ def stop(self) -> None:
+ with self._lock:
+ self._buckets.clear()
diff --git a/posthog/client.py b/posthog/client.py
index e653a10e..6fc4481b 100644
--- a/posthog/client.py
+++ b/posthog/client.py
@@ -211,6 +211,10 @@ def __init__(
code_variables_mask_patterns=None,
code_variables_ignore_patterns=None,
in_app_modules: list[str] | None = None,
+ enable_exception_autocapture_rate_limiting=False,
+ exception_autocapture_bucket_size=ExceptionCapture.DEFAULT_BUCKET_SIZE,
+ exception_autocapture_refill_rate=ExceptionCapture.DEFAULT_REFILL_RATE,
+ exception_autocapture_refill_interval_seconds=ExceptionCapture.DEFAULT_REFILL_INTERVAL_SECONDS,
_dedicated_ai_endpoint=False,
):
"""
@@ -273,6 +277,16 @@ def __init__(
capturing code variables.
in_app_modules: Module/package prefixes treated as in-app frames in
captured exceptions.
+ enable_exception_autocapture_rate_limiting: Rate limit
+ autocaptured exceptions client-side with a token bucket per
+ exception type. Disabled by default.
+ exception_autocapture_bucket_size: Maximum burst of autocaptured
+ exceptions allowed per exception type (token bucket size,
+ clamped to 0-100).
+ exception_autocapture_refill_rate: Tokens restored per refill
+ interval for each exception type's bucket.
+ exception_autocapture_refill_interval_seconds: Seconds between
+ token refills for autocaptured exception rate limiting.
Examples:
```python
@@ -330,6 +344,14 @@ def __init__(
self.super_properties = super_properties
self.enable_exception_autocapture = enable_exception_autocapture
self.log_captured_exceptions = log_captured_exceptions
+ self.enable_exception_autocapture_rate_limiting = (
+ enable_exception_autocapture_rate_limiting
+ )
+ self.exception_autocapture_bucket_size = exception_autocapture_bucket_size
+ self.exception_autocapture_refill_rate = exception_autocapture_refill_rate
+ self.exception_autocapture_refill_interval_seconds = (
+ exception_autocapture_refill_interval_seconds
+ )
self.exception_capture = None
self.privacy_mode = privacy_mode
self.enable_local_evaluation = enable_local_evaluation
@@ -377,7 +399,13 @@ def __init__(
self._set_before_send(before_send)
if self.enable_exception_autocapture:
- self.exception_capture = ExceptionCapture(self)
+ self.exception_capture = ExceptionCapture(
+ self,
+ rate_limiting_enabled=self.enable_exception_autocapture_rate_limiting,
+ bucket_size=self.exception_autocapture_bucket_size,
+ refill_rate=self.exception_autocapture_refill_rate,
+ refill_interval_seconds=self.exception_autocapture_refill_interval_seconds,
+ )
if sync_mode:
self.consumers = None
diff --git a/posthog/exception_capture.py b/posthog/exception_capture.py
index 9d055351..c35f14b2 100644
--- a/posthog/exception_capture.py
+++ b/posthog/exception_capture.py
@@ -9,23 +9,48 @@
import threading
from typing import TYPE_CHECKING
+from posthog.bucketed_rate_limiter import BucketedRateLimiter
+from posthog.exception_utils import walk_exception_chain
+
if TYPE_CHECKING:
from posthog.client import Client
class ExceptionCapture:
- # TODO: Add client side rate limiting to prevent spamming the server with exceptions
-
log = logging.getLogger("posthog")
- def __init__(self, client: "Client"):
+ # more generous defaults than the browser SDK (10, 1, 10) because one
+ # server process aggregates exceptions across many users' requests
+ DEFAULT_BUCKET_SIZE = 50
+ DEFAULT_REFILL_RATE = 10
+ DEFAULT_REFILL_INTERVAL_SECONDS = 10
+
+ def __init__(
+ self,
+ client: "Client",
+ rate_limiting_enabled=False,
+ bucket_size=DEFAULT_BUCKET_SIZE,
+ refill_rate=DEFAULT_REFILL_RATE,
+ refill_interval_seconds=DEFAULT_REFILL_INTERVAL_SECONDS,
+ ):
self.client = client
self.original_excepthook = sys.excepthook
sys.excepthook = self.exception_handler
threading.excepthook = self.thread_exception_handler
+ # opt-in client-side rate limiting: per exception type, allow a burst
+ # of captures, then refill over time
+ self._rate_limiter = None
+ if rate_limiting_enabled:
+ self._rate_limiter = BucketedRateLimiter(
+ bucket_size=bucket_size,
+ refill_rate=refill_rate,
+ refill_interval_seconds=refill_interval_seconds,
+ )
def close(self):
sys.excepthook = self.original_excepthook
+ if self._rate_limiter is not None:
+ self._rate_limiter.stop()
def exception_handler(self, exc_type, exc_value, exc_traceback):
# don't affect default behaviour.
@@ -44,7 +69,38 @@ def exception_receiver(self, exc_info, extra_properties):
def capture_exception(self, exception, metadata=None):
try:
+ if self._rate_limiter is not None:
+ exception_type = self._exception_type(exception)
+ if self._rate_limiter.consume_rate_limit(exception_type):
+ self.log.info(
+ f"Skipping exception capture because of client rate limiting. exception={exception_type}"
+ )
+ return
+
distinct_id = metadata.get("distinct_id") if metadata else None
self.client.capture_exception(exception, distinct_id=distinct_id)
except Exception as e:
self.log.exception(f"Failed to capture exception: {e}")
+
+ @staticmethod
+ def _exception_type(exception):
+ if isinstance(exception, tuple):
+ exc_info = exception
+ else:
+ exc_info = (
+ type(exception),
+ exception,
+ getattr(exception, "__traceback__", None),
+ )
+
+ # PostHog groups exceptions by the root cause of the chain:
+ # exceptions_from_error_tuple reverses the walked chain, so
+ # $exception_list[0].type is the deepest cause, not the wrapping
+ # exception. Key on that same type so rate-limit buckets line up with
+ # server-side grouping (e.g. `raise RuntimeError from ZeroDivisionError`
+ # is keyed on ZeroDivisionError, not RuntimeError).
+ exc_type = exc_info[0]
+ for chained_type, _, _ in walk_exception_chain(exc_info):
+ exc_type = chained_type
+
+ return getattr(exc_type, "__name__", None) or "Exception"
diff --git a/posthog/test/test_bucketed_rate_limiter.py b/posthog/test/test_bucketed_rate_limiter.py
new file mode 100644
index 00000000..93ec96dc
--- /dev/null
+++ b/posthog/test/test_bucketed_rate_limiter.py
@@ -0,0 +1,217 @@
+import threading
+from unittest.mock import MagicMock
+
+import pytest
+
+from posthog.bucketed_rate_limiter import BucketedRateLimiter
+
+
+class FakeClock:
+ def __init__(self, start=0.0):
+ self.now = float(start)
+
+ def advance(self, seconds):
+ self.now += seconds
+
+ def __call__(self):
+ return self.now
+
+
+def make_limiter(
+ clock, bucket_size=10, refill_rate=1, refill_interval_seconds=1, **kwargs
+):
+ return BucketedRateLimiter(
+ bucket_size=bucket_size,
+ refill_rate=refill_rate,
+ refill_interval_seconds=refill_interval_seconds,
+ clock=clock,
+ **kwargs,
+ )
+
+
+def test_not_rate_limited_by_default():
+ assert make_limiter(FakeClock()).consume_rate_limit("ResizeObserver") is False
+
+
+@pytest.mark.parametrize("bucket_size", [1, 5, 10, 50])
+def test_exhausts_bucket_after_bucket_size_consumptions(bucket_size):
+ limiter = make_limiter(FakeClock(), bucket_size=bucket_size)
+
+ # the call that drains the bucket is itself rate limited, so
+ # bucket_size - 1 events pass
+ for _ in range(bucket_size - 1):
+ assert limiter.consume_rate_limit("test") is False
+
+ assert limiter.consume_rate_limit("test") is True
+ # can check the same bucket more than once
+ assert limiter.consume_rate_limit("test") is True
+
+
+def test_refills_tokens_based_on_elapsed_time():
+ clock = FakeClock()
+ limiter = make_limiter(clock)
+
+ for _ in range(9):
+ assert limiter.consume_rate_limit("key") is False
+ assert limiter.consume_rate_limit("key") is True
+
+ clock.advance(2)
+
+ assert limiter.consume_rate_limit("key") is False
+ assert limiter._buckets["key"].tokens == 1
+
+
+def test_refills_to_bucket_size_maximum():
+ clock = FakeClock()
+ limiter = make_limiter(clock)
+ limiter.consume_rate_limit("key")
+
+ clock.advance(20)
+
+ limiter.consume_rate_limit("key")
+ assert limiter._buckets["key"].tokens == 9
+
+
+def test_partial_refill_intervals_do_not_refill_tokens():
+ clock = FakeClock()
+ limiter = make_limiter(clock)
+
+ for _ in range(9):
+ limiter.consume_rate_limit("test")
+
+ clock.advance(0.999)
+
+ limiter.consume_rate_limit("test")
+ assert limiter._buckets["test"].tokens == 0
+
+
+@pytest.mark.parametrize(
+ "refill_rate, intervals, tokens_left, expected",
+ [
+ (1, 1, 9, 9),
+ (2, 1, 9, 9),
+ (1, 2, 9, 9),
+ (3, 1, 5, 7),
+ (2, 2, 5, 8),
+ ],
+)
+def test_refill_rates(refill_rate, intervals, tokens_left, expected):
+ clock = FakeClock()
+ limiter = make_limiter(clock, refill_rate=refill_rate)
+
+ for _ in range(10 - tokens_left):
+ limiter.consume_rate_limit("test")
+
+ clock.advance(intervals)
+
+ limiter.consume_rate_limit("test")
+ assert limiter._buckets["test"].tokens == expected
+
+
+def test_different_keys_maintain_separate_buckets():
+ limiter = make_limiter(FakeClock())
+
+ for _ in range(9):
+ limiter.consume_rate_limit("bucket1")
+
+ assert limiter.consume_rate_limit("bucket1") is True
+ assert limiter.consume_rate_limit("bucket2") is False
+
+ assert limiter._buckets["bucket1"].tokens == 0
+ assert limiter._buckets["bucket2"].tokens == 9
+
+
+def test_invokes_callback_once_when_bucket_reaches_zero():
+ callback = MagicMock()
+ limiter = make_limiter(FakeClock(), bucket_size=3, on_bucket_rate_limited=callback)
+
+ limiter.consume_rate_limit("test")
+ limiter.consume_rate_limit("test")
+ callback.assert_not_called()
+
+ limiter.consume_rate_limit("test")
+ callback.assert_called_once_with("test")
+
+ # not invoked again while the bucket stays empty
+ limiter.consume_rate_limit("test")
+ callback.assert_called_once()
+
+
+def test_invokes_callback_again_after_refill_and_re_exhaustion():
+ callback = MagicMock()
+ clock = FakeClock()
+ limiter = make_limiter(clock, bucket_size=2, on_bucket_rate_limited=callback)
+
+ limiter.consume_rate_limit("test")
+ limiter.consume_rate_limit("test")
+ assert callback.call_count == 1
+
+ clock.advance(2)
+
+ limiter.consume_rate_limit("test")
+ limiter.consume_rate_limit("test")
+ assert callback.call_count == 2
+
+
+def test_stop_clears_all_buckets_and_resets_state():
+ clock = FakeClock()
+ limiter = make_limiter(clock)
+
+ for _ in range(9):
+ limiter.consume_rate_limit("test")
+ limiter.consume_rate_limit("other")
+ assert len(limiter._buckets) == 2
+
+ limiter.stop()
+ assert len(limiter._buckets) == 0
+
+ assert limiter.consume_rate_limit("test") is False
+ assert limiter._buckets["test"].tokens == 9
+
+
+def test_last_access_advances_by_complete_intervals_preserving_fraction():
+ clock = FakeClock()
+ limiter = make_limiter(clock)
+
+ limiter.consume_rate_limit("test")
+ assert limiter._buckets["test"].last_access == 0.0
+ assert limiter._buckets["test"].tokens == 9
+
+ clock.advance(0.5)
+ limiter.consume_rate_limit("test")
+ assert limiter._buckets["test"].last_access == 0.0
+ assert limiter._buckets["test"].tokens == 8
+
+ clock.advance(0.6)
+ limiter.consume_rate_limit("test")
+ assert limiter._buckets["test"].last_access == 1.0
+ assert limiter._buckets["test"].tokens == 8
+
+
+def test_clamps_out_of_range_options():
+ clock = FakeClock()
+ limiter = make_limiter(
+ clock, bucket_size=1000, refill_rate=-5, refill_interval_seconds="nope"
+ )
+
+ assert limiter._bucket_size == 100
+ assert limiter._refill_rate == 0
+ assert limiter._refill_interval == 86400.0
+
+
+def test_thread_safety_allows_exactly_bucket_size_minus_one():
+ limiter = make_limiter(FakeClock(), bucket_size=50)
+ allowed = []
+
+ def consume():
+ for _ in range(20):
+ if limiter.consume_rate_limit("shared") is False:
+ allowed.append(1)
+
+ threads = [threading.Thread(target=consume) for _ in range(10)]
+ for t in threads:
+ t.start()
+ for t in threads:
+ t.join()
+
+ assert len(allowed) == 49
diff --git a/posthog/test/test_exception_capture.py b/posthog/test/test_exception_capture.py
index 3d2f8d08..702c7bbb 100644
--- a/posthog/test/test_exception_capture.py
+++ b/posthog/test/test_exception_capture.py
@@ -1,10 +1,132 @@
import subprocess
import sys
from textwrap import dedent
+from unittest.mock import MagicMock
import pytest
+def _exc_info(error):
+ try:
+ raise error
+ except BaseException:
+ return sys.exc_info()
+
+
+def _chained_exc_info(cause, wrapper):
+ try:
+ raise wrapper from cause
+ except BaseException:
+ return sys.exc_info()
+
+
+def test_rate_limiting_is_disabled_by_default():
+ from posthog.exception_capture import ExceptionCapture
+
+ client = MagicMock()
+ capture = ExceptionCapture(client)
+ try:
+ assert capture._rate_limiter is None
+
+ for _ in range(100):
+ capture.capture_exception(_exc_info(ValueError("boom")))
+ assert client.capture_exception.call_count == 100
+ finally:
+ capture.close()
+
+
+def test_rate_limiting_default_configuration_when_enabled():
+ from posthog.exception_capture import ExceptionCapture
+
+ capture = ExceptionCapture(MagicMock(), rate_limiting_enabled=True)
+ try:
+ assert capture._rate_limiter._bucket_size == 50
+ assert capture._rate_limiter._refill_rate == 10
+ assert capture._rate_limiter._refill_interval == 10
+ finally:
+ capture.close()
+
+
+def test_rate_limiting_is_configurable():
+ from posthog.exception_capture import ExceptionCapture
+
+ capture = ExceptionCapture(
+ MagicMock(),
+ rate_limiting_enabled=True,
+ bucket_size=3,
+ refill_rate=2,
+ refill_interval_seconds=5,
+ )
+ try:
+ assert capture._rate_limiter._bucket_size == 3
+ assert capture._rate_limiter._refill_rate == 2
+ assert capture._rate_limiter._refill_interval == 5
+ finally:
+ capture.close()
+
+
+def test_client_passes_rate_limiter_configuration_through():
+ from posthog.client import Client
+
+ client = Client(
+ "phc_test",
+ sync_mode=True,
+ disabled=True,
+ enable_exception_autocapture=True,
+ enable_exception_autocapture_rate_limiting=True,
+ exception_autocapture_bucket_size=3,
+ exception_autocapture_refill_rate=2,
+ exception_autocapture_refill_interval_seconds=5,
+ )
+ try:
+ limiter = client.exception_capture._rate_limiter
+ assert limiter._bucket_size == 3
+ assert limiter._refill_rate == 2
+ assert limiter._refill_interval == 5
+ finally:
+ client.shutdown()
+
+
+def test_rate_limits_per_exception_type():
+ from posthog.exception_capture import ExceptionCapture
+
+ client = MagicMock()
+ capture = ExceptionCapture(client, rate_limiting_enabled=True, bucket_size=10)
+ try:
+ for _ in range(15):
+ capture.capture_exception(_exc_info(ValueError("boom")))
+
+ # bucket size 10 -> 9 captured, the rest rate limited
+ assert client.capture_exception.call_count == 9
+
+ # a different exception type has its own bucket
+ capture.capture_exception(_exc_info(ZeroDivisionError("zero")))
+ assert client.capture_exception.call_count == 10
+ finally:
+ capture.close()
+
+
+def test_rate_limit_keys_on_root_cause_of_chained_exceptions():
+ from posthog.exception_capture import ExceptionCapture
+
+ # PostHog groups by the root cause ($exception_list[0].type), so chained
+ # exceptions sharing a wrapper type but differing in their cause must land
+ # in separate buckets rather than collapsing under the wrapper.
+ client = MagicMock()
+ capture = ExceptionCapture(client, rate_limiting_enabled=True, bucket_size=2)
+ try:
+ capture.capture_exception(
+ _chained_exc_info(ZeroDivisionError(), RuntimeError("wrapped"))
+ )
+ capture.capture_exception(
+ _chained_exc_info(KeyError(), RuntimeError("wrapped"))
+ )
+
+ assert client.capture_exception.call_count == 2
+ finally:
+ capture.close()
+
+
def test_excepthook(tmpdir):
app = tmpdir.join("app.py")
app.write(
From fbff5adb11e04f1e850acec96acef50e5b4d2a15 Mon Sep 17 00:00:00 2001
From: "releaser-posthog-python[bot]"
<262414804+releaser-posthog-python[bot]@users.noreply.github.com>
Date: Mon, 15 Jun 2026 10:02:08 +0000
Subject: [PATCH 2/8] chore: Release v7.19.0 [skip ci]
---
.sampo/changesets/exception-autocapture-rate-limiting.md | 5 -----
CHANGELOG.md | 6 ++++++
posthog/version.py | 2 +-
pyproject.toml | 2 +-
uv.lock | 2 +-
5 files changed, 9 insertions(+), 8 deletions(-)
delete mode 100644 .sampo/changesets/exception-autocapture-rate-limiting.md
diff --git a/.sampo/changesets/exception-autocapture-rate-limiting.md b/.sampo/changesets/exception-autocapture-rate-limiting.md
deleted file mode 100644
index ae6743e7..00000000
--- a/.sampo/changesets/exception-autocapture-rate-limiting.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-pypi/posthog: minor
----
-
-Add opt-in client-side rate limiting for exception autocapture, using the same token bucket algorithm as the posthog-js and posthog-node SDKs: a bucket per exception type allows a burst of captures, then refills over time. Rate-limited exceptions are skipped before they reach the ingestion queue. Disabled by default; enable with the new `enable_exception_autocapture_rate_limiting` client option and tune via `exception_autocapture_bucket_size` (default 50), `exception_autocapture_refill_rate` (default 10), and `exception_autocapture_refill_interval_seconds` (default 10).
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e63ad969..9bfbf832 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# posthog
+## 7.19.0 — 2026-06-15
+
+### Minor changes
+
+- [b9f3208](https://github.com/posthog/posthog-python/commit/b9f320808f44832fc3b4cdd6bcf0e0aad194b76d) Add opt-in client-side rate limiting for exception autocapture, using the same token bucket algorithm as the posthog-js and posthog-node SDKs: a bucket per exception type allows a burst of captures, then refills over time. Rate-limited exceptions are skipped before they reach the ingestion queue. Disabled by default; enable with the new `enable_exception_autocapture_rate_limiting` client option and tune via `exception_autocapture_bucket_size` (default 50), `exception_autocapture_refill_rate` (default 10), and `exception_autocapture_refill_interval_seconds` (default 10). — Thanks @hpouillot!
+
## 7.18.3 — 2026-06-12
### Patch changes
diff --git a/posthog/version.py b/posthog/version.py
index 45f23220..69f0099d 100644
--- a/posthog/version.py
+++ b/posthog/version.py
@@ -1 +1 @@
-VERSION = "7.18.3"
+VERSION = "7.19.0"
diff --git a/pyproject.toml b/pyproject.toml
index 9717aec2..2998c88e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "posthog"
-version = "7.18.3"
+version = "7.19.0"
description = "Integrate PostHog into any python application."
authors = [{ name = "PostHog", email = "engineering@posthog.com" }]
maintainers = [{ name = "PostHog", email = "engineering@posthog.com" }]
diff --git a/uv.lock b/uv.lock
index a0f7658f..8ee98040 100644
--- a/uv.lock
+++ b/uv.lock
@@ -2169,7 +2169,7 @@ wheels = [
[[package]]
name = "posthog"
-version = "7.18.3"
+version = "7.19.0"
source = { editable = "." }
dependencies = [
{ name = "backoff" },
From 62d09a604811a2273feb0f0bedd4c3c15b334772 Mon Sep 17 00:00:00 2001
From: "releaser-posthog-python[bot]"
<262414804+releaser-posthog-python[bot]@users.noreply.github.com>
Date: Mon, 15 Jun 2026 10:02:40 +0000
Subject: [PATCH 3/8] Update generated references
---
.../posthog-python-references-7.19.0.json | 2948 +++++++++++++++++
.../posthog-python-references-latest.json | 26 +-
2 files changed, 2973 insertions(+), 1 deletion(-)
create mode 100644 references/posthog-python-references-7.19.0.json
diff --git a/references/posthog-python-references-7.19.0.json b/references/posthog-python-references-7.19.0.json
new file mode 100644
index 00000000..efe2ea4b
--- /dev/null
+++ b/references/posthog-python-references-7.19.0.json
@@ -0,0 +1,2948 @@
+{
+ "id": "posthog-python",
+ "hogRef": "0.3",
+ "info": {
+ "version": "7.19.0",
+ "id": "posthog-python",
+ "title": "PostHog Python SDK",
+ "description": "Integrate PostHog into any python application.",
+ "slugPrefix": "posthog-python",
+ "specUrl": "https://github.com/PostHog/posthog-python"
+ },
+ "types": [
+ {
+ "id": "FeatureFlag",
+ "name": "FeatureFlag",
+ "path": "posthog.types.FeatureFlag",
+ "properties": [
+ {
+ "name": "key",
+ "type": "str",
+ "description": "Field: key"
+ },
+ {
+ "name": "enabled",
+ "type": "bool",
+ "description": "Field: enabled"
+ },
+ {
+ "name": "variant",
+ "type": "Optional[str]",
+ "description": "Field: variant"
+ },
+ {
+ "name": "reason",
+ "type": "Optional[FlagReason]",
+ "description": "Field: reason"
+ },
+ {
+ "name": "metadata",
+ "type": "Union[FlagMetadata, LegacyFlagMetadata]",
+ "description": "Field: metadata"
+ }
+ ],
+ "example": ""
+ },
+ {
+ "id": "FeatureFlagResult",
+ "name": "FeatureFlagResult",
+ "path": "posthog.types.FeatureFlagResult",
+ "properties": [
+ {
+ "name": "key",
+ "type": "str",
+ "description": "Field: key"
+ },
+ {
+ "name": "enabled",
+ "type": "bool",
+ "description": "Field: enabled"
+ },
+ {
+ "name": "variant",
+ "type": "Optional[str]",
+ "description": "Field: variant"
+ },
+ {
+ "name": "payload",
+ "type": "Optional[Any]",
+ "description": "Field: payload"
+ },
+ {
+ "name": "reason",
+ "type": "Optional[str]",
+ "description": "Field: reason"
+ }
+ ],
+ "example": ""
+ },
+ {
+ "id": "FlagMetadata",
+ "name": "FlagMetadata",
+ "path": "posthog.types.FlagMetadata",
+ "properties": [
+ {
+ "name": "id",
+ "type": "int",
+ "description": "Field: id"
+ },
+ {
+ "name": "payload",
+ "type": "Optional[str]",
+ "description": "Field: payload"
+ },
+ {
+ "name": "version",
+ "type": "int",
+ "description": "Field: version"
+ },
+ {
+ "name": "description",
+ "type": "str",
+ "description": "Field: description"
+ }
+ ],
+ "example": ""
+ },
+ {
+ "id": "FlagReason",
+ "name": "FlagReason",
+ "path": "posthog.types.FlagReason",
+ "properties": [
+ {
+ "name": "code",
+ "type": "str",
+ "description": "Field: code"
+ },
+ {
+ "name": "condition_index",
+ "type": "Optional[int]",
+ "description": "Field: condition_index"
+ },
+ {
+ "name": "description",
+ "type": "str",
+ "description": "Field: description"
+ }
+ ],
+ "example": ""
+ },
+ {
+ "id": "FlagsAndPayloads",
+ "name": "FlagsAndPayloads",
+ "path": "posthog.types.FlagsAndPayloads",
+ "properties": [
+ {
+ "name": "featureFlags",
+ "type": "Optional[dict[str, Union[bool, str]]]",
+ "description": "Field: featureFlags"
+ },
+ {
+ "name": "featureFlagPayloads",
+ "type": "Optional[dict[str, Any]]",
+ "description": "Field: featureFlagPayloads"
+ }
+ ],
+ "example": ""
+ },
+ {
+ "id": "FlagsResponse",
+ "name": "FlagsResponse",
+ "path": "posthog.types.FlagsResponse",
+ "properties": [
+ {
+ "name": "flags",
+ "type": "dict[str, FeatureFlag]",
+ "description": "Field: flags"
+ },
+ {
+ "name": "errorsWhileComputingFlags",
+ "type": "bool",
+ "description": "Field: errorsWhileComputingFlags"
+ },
+ {
+ "name": "requestId",
+ "type": "str",
+ "description": "Field: requestId"
+ },
+ {
+ "name": "quotaLimit",
+ "type": "Optional[list[str]]",
+ "description": "Field: quotaLimit"
+ },
+ {
+ "name": "evaluatedAt",
+ "type": "Optional[int]",
+ "description": "Field: evaluatedAt"
+ }
+ ],
+ "example": ""
+ },
+ {
+ "id": "LegacyFlagMetadata",
+ "name": "LegacyFlagMetadata",
+ "path": "posthog.types.LegacyFlagMetadata",
+ "properties": [
+ {
+ "name": "payload",
+ "type": "Any",
+ "description": "Field: payload"
+ }
+ ],
+ "example": ""
+ },
+ {
+ "id": "SendFeatureFlagsOptions",
+ "name": "SendFeatureFlagsOptions",
+ "path": "posthog.types.SendFeatureFlagsOptions",
+ "properties": [
+ {
+ "name": "should_send",
+ "type": "bool",
+ "description": "Field: should_send"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "type": "Optional[bool]",
+ "description": "Field: only_evaluate_locally"
+ },
+ {
+ "name": "person_properties",
+ "type": "Optional[dict[str, Any]]",
+ "description": "Field: person_properties"
+ },
+ {
+ "name": "group_properties",
+ "type": "Optional[dict[str, dict[str, Any]]]",
+ "description": "Field: group_properties"
+ },
+ {
+ "name": "flag_keys_filter",
+ "type": "Optional[list[str]]",
+ "description": "Field: flag_keys_filter"
+ }
+ ],
+ "example": ""
+ },
+ {
+ "id": "OptionalCaptureArgs",
+ "name": "OptionalCaptureArgs",
+ "path": "posthog.args.OptionalCaptureArgs",
+ "properties": [
+ {
+ "name": "distinct_id",
+ "type": "NotRequired[Union[Number, str, UUID, int, any]]",
+ "description": "Field: distinct_id"
+ },
+ {
+ "name": "properties",
+ "type": "NotRequired[Optional[dict[str, Any]]]",
+ "description": "Field: properties"
+ },
+ {
+ "name": "timestamp",
+ "type": "NotRequired[Union[datetime, str, any]]",
+ "description": "Field: timestamp"
+ },
+ {
+ "name": "uuid",
+ "type": "NotRequired[Optional[str]]",
+ "description": "Field: uuid"
+ },
+ {
+ "name": "groups",
+ "type": "NotRequired[Optional[dict[str, str]]]",
+ "description": "Field: groups"
+ },
+ {
+ "name": "flags",
+ "type": "NotRequired[Optional[ForwardRef('FeatureFlagEvaluations')]]",
+ "description": "Field: flags"
+ },
+ {
+ "name": "send_feature_flags",
+ "type": "NotRequired[Union[bool, SendFeatureFlagsOptions, any]]",
+ "description": "Field: send_feature_flags"
+ },
+ {
+ "name": "disable_geoip",
+ "type": "NotRequired[Optional[bool]]",
+ "description": "Field: disable_geoip"
+ }
+ ],
+ "example": ""
+ },
+ {
+ "id": "OptionalSetArgs",
+ "name": "OptionalSetArgs",
+ "path": "posthog.args.OptionalSetArgs",
+ "properties": [
+ {
+ "name": "distinct_id",
+ "type": "NotRequired[Union[Number, str, UUID, int, any]]",
+ "description": "Field: distinct_id"
+ },
+ {
+ "name": "properties",
+ "type": "NotRequired[Optional[dict[str, Any]]]",
+ "description": "Field: properties"
+ },
+ {
+ "name": "timestamp",
+ "type": "NotRequired[Union[datetime, str, any]]",
+ "description": "Field: timestamp"
+ },
+ {
+ "name": "uuid",
+ "type": "NotRequired[Optional[str]]",
+ "description": "Field: uuid"
+ },
+ {
+ "name": "disable_geoip",
+ "type": "NotRequired[Optional[bool]]",
+ "description": "Field: disable_geoip"
+ }
+ ],
+ "example": ""
+ },
+ {
+ "id": "SendFeatureFlagsOptions",
+ "name": "SendFeatureFlagsOptions",
+ "path": "posthog.types.SendFeatureFlagsOptions",
+ "properties": [
+ {
+ "name": "should_send",
+ "type": "bool",
+ "description": "Field: should_send"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "type": "Optional[bool]",
+ "description": "Field: only_evaluate_locally"
+ },
+ {
+ "name": "person_properties",
+ "type": "Optional[dict[str, Any]]",
+ "description": "Field: person_properties"
+ },
+ {
+ "name": "group_properties",
+ "type": "Optional[dict[str, dict[str, Any]]]",
+ "description": "Field: group_properties"
+ },
+ {
+ "name": "flag_keys_filter",
+ "type": "Optional[list[str]]",
+ "description": "Field: flag_keys_filter"
+ }
+ ],
+ "example": ""
+ }
+ ],
+ "classes": [
+ {
+ "id": "PostHog",
+ "title": "PostHog",
+ "description": "This is the SDK reference for the PostHog Python SDK. You can learn more about example usage in the [Python SDK documentation](/docs/libraries/python). You can also follow [Flask](/docs/libraries/flask) and [Django](/docs/libraries/django) guides to integrate PostHog into your project.",
+ "functions": [
+ {
+ "id": "__init__",
+ "title": "Client",
+ "description": "Initialize a new PostHog client instance.",
+ "details": "",
+ "category": "Initialization",
+ "params": [
+ {
+ "name": "project_api_key",
+ "description": "PostHog project API key/token.",
+ "isOptional": true,
+ "type": "str"
+ },
+ {
+ "name": "host",
+ "description": "PostHog host. Defaults to the US ingestion endpoint when not set. App hosts such as ``https://us.posthog.com`` are mapped to the corresponding ingestion host.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "debug",
+ "description": "Enable verbose SDK logging and re-raise errors from public API methods.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "max_queue_size",
+ "description": "Maximum number of events buffered before upload.",
+ "isOptional": false,
+ "type": "int"
+ },
+ {
+ "name": "send",
+ "description": "If False, queueing succeeds but events are not sent.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "on_error",
+ "description": "Optional callback invoked by background consumers when an upload fails.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "flush_at",
+ "description": "Number of queued events that triggers a batch upload.",
+ "isOptional": false,
+ "type": "int"
+ },
+ {
+ "name": "flush_interval",
+ "description": "Maximum seconds a background consumer waits before flushing a partial batch.",
+ "isOptional": false,
+ "type": "float"
+ },
+ {
+ "name": "gzip",
+ "description": "Whether to gzip event upload payloads.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "max_retries",
+ "description": "Number of upload retries for background consumers.",
+ "isOptional": false,
+ "type": "int"
+ },
+ {
+ "name": "sync_mode",
+ "description": "If True, send each event synchronously instead of using background worker threads.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "timeout",
+ "description": "HTTP request timeout in seconds for event uploads.",
+ "isOptional": false,
+ "type": "int"
+ },
+ {
+ "name": "thread",
+ "description": "Number of background consumer threads.",
+ "isOptional": false,
+ "type": "int"
+ },
+ {
+ "name": "poll_interval",
+ "description": "Seconds between local feature flag definition refreshes.",
+ "isOptional": false,
+ "type": "int"
+ },
+ {
+ "name": "personal_api_key",
+ "description": "Personal API key used for local feature flag evaluation and remote config payloads.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "disabled",
+ "description": "If True, disable captures and API requests. Useful in tests.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable server-side GeoIP enrichment. Defaults to True.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "is_server",
+ "description": "Whether events are emitted from a server-side runtime. Defaults to True; set to False when using the SDK as a client/CLI so the device OS is attributed to the person normally.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "historical_migration",
+ "description": "Mark events as historical migration imports.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "feature_flags_request_timeout_seconds",
+ "description": "Timeout in seconds for feature flag and remote config requests.",
+ "isOptional": false,
+ "type": "int"
+ },
+ {
+ "name": "super_properties",
+ "description": "Properties merged into every captured event.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "enable_exception_autocapture",
+ "description": "Automatically capture uncaught exceptions.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "log_captured_exceptions",
+ "description": "Also log exceptions captured by error tracking.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "project_root",
+ "description": "Root path used to determine in-app stack frames for captured exceptions. Defaults to the current working directory.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "privacy_mode",
+ "description": "For AI observability, capture usage metadata without prompt inputs or outputs.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "before_send",
+ "description": "Optional callback that can modify or drop events before upload. Return ``None`` to drop an event.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "flag_fallback_cache_url",
+ "description": "Optional feature flag fallback cache URL, such as ``memory://local/?ttl=300&size=10000`` or a Redis URL.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "enable_local_evaluation",
+ "description": "Whether to poll feature flag definitions for local evaluation when a personal API key is configured.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "flag_definition_cache_provider",
+ "description": "Optional external cache provider for sharing feature flag definitions across workers.",
+ "isOptional": true,
+ "type": "FlagDefinitionCacheProvider"
+ },
+ {
+ "name": "capture_exception_code_variables",
+ "description": "Capture local variable values on exception stack frames.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "code_variables_mask_patterns",
+ "description": "Variable-name patterns to mask when capturing code variables.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "code_variables_ignore_patterns",
+ "description": "Variable-name patterns to omit when capturing code variables.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "in_app_modules",
+ "description": "Module/package prefixes treated as in-app frames in captured exceptions.",
+ "isOptional": false,
+ "type": "UnionType[list[str], any]"
+ },
+ {
+ "name": "enable_exception_autocapture_rate_limiting",
+ "description": "Rate limit autocaptured exceptions client-side with a token bucket per exception type. Disabled by default.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "exception_autocapture_bucket_size",
+ "description": "Maximum burst of autocaptured exceptions allowed per exception type (token bucket size, clamped to 0-100).",
+ "isOptional": false,
+ "type": "int"
+ },
+ {
+ "name": "exception_autocapture_refill_rate",
+ "description": "Tokens restored per refill interval for each exception type's bucket.",
+ "isOptional": false,
+ "type": "int"
+ },
+ {
+ "name": "exception_autocapture_refill_interval_seconds",
+ "description": "Seconds between token refills for autocaptured exception rate limiting.",
+ "isOptional": false,
+ "type": "int"
+ },
+ {
+ "name": "_dedicated_ai_endpoint",
+ "description": "",
+ "isOptional": false,
+ "type": "bool"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import Posthog\n\nposthog = Posthog('', host='')"
+ }
+ ]
+ },
+ {
+ "id": "alias",
+ "title": "alias",
+ "description": "Create an alias between two distinct IDs.",
+ "details": "",
+ "category": "Identification",
+ "params": [
+ {
+ "name": "previous_id",
+ "description": "The previous distinct ID.",
+ "isOptional": true,
+ "type": "str"
+ },
+ {
+ "name": "distinct_id",
+ "description": "The new distinct ID to alias to.",
+ "isOptional": true,
+ "type": "str"
+ },
+ {
+ "name": "timestamp",
+ "description": "The timestamp of the event.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "uuid",
+ "description": "A unique identifier for the event.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this event.",
+ "isOptional": false,
+ "type": "any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "posthog.alias(previous_id='distinct_id', distinct_id='alias_id')"
+ }
+ ]
+ },
+ {
+ "id": "capture",
+ "title": "capture",
+ "description": "Captures an event manually. [Learn about capture best practices](https://posthog.com/docs/product-analytics/capture-events)",
+ "details": "",
+ "category": "Capture",
+ "params": [
+ {
+ "name": "event",
+ "description": "The event name to capture.",
+ "isOptional": true,
+ "type": "str"
+ },
+ {
+ "name": "kwargs",
+ "description": "",
+ "isOptional": true,
+ "type": "Unpack[OptionalCaptureArgs]"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[str]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Anonymous event",
+ "code": "# Anonymous event\nposthog.capture('some-anon-event')"
+ },
+ {
+ "id": "example_2",
+ "name": "Context usage",
+ "code": "# Context usage\nfrom posthog import identify_context, new_context\nwith new_context():\n identify_context('distinct_id_of_the_user')\n posthog.capture('user_signed_up')\n posthog.capture('user_logged_in')\n posthog.capture('some-custom-action', distinct_id='distinct_id_of_the_user')"
+ },
+ {
+ "id": "example_3",
+ "name": "Set event properties",
+ "code": "# Set event properties\nposthog.capture(\n \"user_signed_up\",\n distinct_id=\"distinct_id_of_the_user\",\n properties={\n \"login_type\": \"email\",\n \"is_free_trial\": \"true\"\n }\n)"
+ },
+ {
+ "id": "example_4",
+ "name": "Page view event",
+ "code": "# Page view event\nposthog.capture('$pageview', distinct_id=\"distinct_id_of_the_user\", properties={'$current_url': 'https://example.com'})"
+ }
+ ]
+ },
+ {
+ "id": "capture_exception",
+ "title": "capture_exception",
+ "description": "Capture an exception for error tracking.",
+ "details": "",
+ "category": "Error Tracking",
+ "params": [
+ {
+ "name": "exception",
+ "description": "The exception to capture.",
+ "isOptional": true,
+ "type": "BaseException"
+ },
+ {
+ "name": "kwargs",
+ "description": "",
+ "isOptional": true,
+ "type": "Unpack[OptionalCaptureArgs]"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "try:\n # Some code that might fail\n pass\nexcept Exception as e:\n posthog.capture_exception(e, 'user_distinct_id', properties=additional_properties)"
+ }
+ ]
+ },
+ {
+ "id": "evaluate_flags",
+ "title": "evaluate_flags",
+ "description": "Evaluate all feature flags for a user in a single call and return a :class:`FeatureFlagEvaluations` snapshot. Branch on ``.is_enabled()`` / ``.get_flag()`` and pass the same snapshot to :meth:`capture` via the ``flags`` option so events carry the exact flag values the code branched on. Prefer this over repeated ``get_feature_flag()`` calls and over ``capture(send_feature_flags=True)`` \u2014 it consolidates flag evaluation into a single ``/flags`` request per incoming request. Local evaluation is transparent: when the poller resolves a flag, the snapshot's ``$feature_flag_called`` events are tagged ``locally_evaluated=True`` and reason ``\"Evaluated locally\"``.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "distinct_id",
+ "description": "The user's distinct ID. If ``None``, falls back to the context distinct_id. If still unresolvable, returns an empty snapshot.",
+ "isOptional": false,
+ "type": "Number"
+ },
+ {
+ "name": "groups",
+ "description": "Mapping of group type to group key.",
+ "isOptional": true,
+ "type": "dict[str, str]"
+ },
+ {
+ "name": "person_properties",
+ "description": "Person properties to use for evaluation.",
+ "isOptional": true,
+ "type": "dict[str, Any]"
+ },
+ {
+ "name": "group_properties",
+ "description": "Group properties keyed by group type.",
+ "isOptional": true,
+ "type": "dict[str, dict[str, Any]]"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "If True, never fall back to remote evaluation \u2014 flags that can't be evaluated locally are simply omitted from the snapshot.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP lookup.",
+ "isOptional": true,
+ "type": "bool"
+ },
+ {
+ "name": "flag_keys",
+ "description": "Optional list of flag keys to scope the underlying ``/flags`` request to a subset.",
+ "isOptional": true,
+ "type": "list[str]"
+ },
+ {
+ "name": "device_id",
+ "description": "Optional device ID override. If not provided, falls back to the context device_id (which may be set via tracing headers). Used by experience-continuity flags to match users across distinct_id changes.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "FeatureFlagEvaluations"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "flags = posthog.evaluate_flags(\n \"user_123\",\n person_properties={\"plan\": \"enterprise\"},\n)\nif flags.is_enabled(\"new-dashboard\"):\n render_new_dashboard()\nposthog.capture(\"page_viewed\", distinct_id=\"user_123\", flags=flags)"
+ }
+ ]
+ },
+ {
+ "id": "feature_enabled",
+ "title": "feature_enabled",
+ "description": "Check if a feature flag is enabled for a user.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "key",
+ "description": "The feature flag key.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID of the user.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "A dictionary of group information.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "A dictionary of person properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "A dictionary of group properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to only evaluate locally.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "send_feature_flag_events",
+ "description": "Whether to send feature flag events.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this request.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "device_id",
+ "description": "The device ID for this request.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "is_my_flag_enabled = posthog.feature_enabled('flag-key', 'distinct_id_of_your_user')\nif is_my_flag_enabled:\n # Do something differently for this user\n # Optional: fetch the payload\n matched_flag_payload = posthog.get_feature_flag_payload('flag-key', 'distinct_id_of_your_user')"
+ }
+ ]
+ },
+ {
+ "id": "feature_flag_definitions",
+ "title": "feature_flag_definitions",
+ "description": "Return feature flag definitions loaded for local evaluation. Returns: The currently loaded feature flag definitions, or ``None`` before local evaluation has loaded definitions.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ }
+ },
+ {
+ "id": "flush",
+ "title": "flush",
+ "description": "Force a flush from the internal queue to the server. Do not use directly, call `shutdown()` instead.",
+ "details": "",
+ "category": null,
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "posthog.capture('event_name')\nposthog.flush() # Ensures the event is sent immediately"
+ }
+ ]
+ },
+ {
+ "id": "get_all_flags",
+ "title": "get_all_flags",
+ "description": "Get all feature flags for a user.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID of the user.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "A dictionary of group information.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "A dictionary of person properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "A dictionary of group properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to only evaluate locally.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this request.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "flag_keys_to_evaluate",
+ "description": "A list of specific flag keys to evaluate. If provided, only these flags will be evaluated, improving performance.",
+ "isOptional": true,
+ "type": "list[str]"
+ },
+ {
+ "name": "device_id",
+ "description": "The device ID for this request.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[dict[str, Union[bool, str]]]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "posthog.get_all_flags('distinct_id_of_your_user')"
+ }
+ ]
+ },
+ {
+ "id": "get_all_flags_and_payloads",
+ "title": "get_all_flags_and_payloads",
+ "description": "Get all feature flags and their payloads for a user.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID of the user.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "A dictionary of group information.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "A dictionary of person properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "A dictionary of group properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to only evaluate locally.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this request.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "flag_keys_to_evaluate",
+ "description": "A list of specific flag keys to evaluate. If provided, only these flags will be evaluated, improving performance.",
+ "isOptional": true,
+ "type": "list[str]"
+ },
+ {
+ "name": "device_id",
+ "description": "The device ID for this request.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "FlagsAndPayloads"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "posthog.get_all_flags_and_payloads('distinct_id_of_your_user')"
+ }
+ ]
+ },
+ {
+ "id": "get_feature_flag",
+ "title": "get_feature_flag",
+ "description": "Get multivariate feature flag value for a user.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "key",
+ "description": "The feature flag key.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID of the user.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "A dictionary of group information.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "A dictionary of person properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "A dictionary of group properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to only evaluate locally.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "send_feature_flag_events",
+ "description": "Whether to send feature flag events.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this request.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "device_id",
+ "description": "The device ID for this request.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Union[bool, str, any]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "enabled_variant = posthog.get_feature_flag('flag-key', 'distinct_id_of_your_user')\nif enabled_variant == 'variant-key': # replace 'variant-key' with the key of your variant\n # Do something differently for this user\n # Optional: fetch the payload\n matched_flag_payload = posthog.get_feature_flag_payload('flag-key', 'distinct_id_of_your_user')"
+ }
+ ]
+ },
+ {
+ "id": "get_feature_flag_payload",
+ "title": "get_feature_flag_payload",
+ "description": "Get the payload for a feature flag.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "key",
+ "description": "The feature flag key.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID of the user.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "match_value",
+ "description": "The specific flag value to get payload for.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "groups",
+ "description": "A dictionary of group information.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "A dictionary of person properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "A dictionary of group properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to only evaluate locally.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "send_feature_flag_events",
+ "description": "Deprecated. Use get_feature_flag() instead if you need events.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this request.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "device_id",
+ "description": "The device ID for this request.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "is_my_flag_enabled = posthog.feature_enabled('flag-key', 'distinct_id_of_your_user')\n\nif is_my_flag_enabled:\n # Do something differently for this user\n # Optional: fetch the payload\n matched_flag_payload = posthog.get_feature_flag_payload('flag-key', 'distinct_id_of_your_user')"
+ }
+ ]
+ },
+ {
+ "id": "get_feature_flag_result",
+ "title": "get_feature_flag_result",
+ "description": "Get a FeatureFlagResult object which contains the flag result and payload for a key by evaluating locally or remotely depending on whether local evaluation is enabled and the flag can be locally evaluated. This also captures the `$feature_flag_called` event unless `send_feature_flag_events` is `False`.",
+ "details": "",
+ "category": null,
+ "params": [
+ {
+ "name": "key",
+ "description": "The feature flag key.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID of the user.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "A dictionary of group information.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "A dictionary of person properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "A dictionary of group properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to only evaluate locally.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "send_feature_flag_events",
+ "description": "Whether to send feature flag events.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this request.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "device_id",
+ "description": "The device ID for this request.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[FeatureFlagResult]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "flag_result = posthog.get_feature_flag_result('flag-key', 'distinct_id_of_your_user')\nif flag_result and flag_result.get_value() == 'variant-key':\n # Do something differently for this user\n # Optional: fetch the payload\n matched_flag_payload = flag_result.payload"
+ }
+ ]
+ },
+ {
+ "id": "get_feature_flags_and_payloads",
+ "title": "get_feature_flags_and_payloads",
+ "description": "Get feature flags and payloads for a user.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID of the user.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "A dictionary of group information.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "A dictionary of person properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "A dictionary of group properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this request.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "flag_keys_to_evaluate",
+ "description": "A list of specific flag keys to evaluate. If provided, only these flags will be evaluated, improving performance.",
+ "isOptional": true,
+ "type": "list[str]"
+ },
+ {
+ "name": "device_id",
+ "description": "The device ID for this request.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "FlagsAndPayloads"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "result = posthog.get_feature_flags_and_payloads('')"
+ }
+ ]
+ },
+ {
+ "id": "get_feature_payloads",
+ "title": "get_feature_payloads",
+ "description": "Get feature flag payloads for a user.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID of the user.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "A dictionary of group information.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "A dictionary of person properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "A dictionary of group properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this request.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "flag_keys_to_evaluate",
+ "description": "A list of specific flag keys to evaluate. If provided, only these flags will be evaluated, improving performance.",
+ "isOptional": true,
+ "type": "list[str]"
+ },
+ {
+ "name": "device_id",
+ "description": "The device ID for this request.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "dict[str, str]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "payloads = posthog.get_feature_payloads('')"
+ }
+ ]
+ },
+ {
+ "id": "get_feature_variants",
+ "title": "get_feature_variants",
+ "description": "Get feature flag variants for a user.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID of the user.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "A dictionary of group information.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "A dictionary of person properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "A dictionary of group properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this request.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "flag_keys_to_evaluate",
+ "description": "A list of specific flag keys to evaluate. If provided, only these flags will be evaluated, improving performance.",
+ "isOptional": true,
+ "type": "list[str]"
+ },
+ {
+ "name": "device_id",
+ "description": "The device ID for this request.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "dict[str, Union[bool, str]]"
+ }
+ },
+ {
+ "id": "get_flags_decision",
+ "title": "get_flags_decision",
+ "description": "Get feature flags decision.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID of the user.",
+ "isOptional": false,
+ "type": "Number"
+ },
+ {
+ "name": "groups",
+ "description": "A dictionary of group information.",
+ "isOptional": true,
+ "type": "dict"
+ },
+ {
+ "name": "person_properties",
+ "description": "A dictionary of person properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "A dictionary of group properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this request.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "flag_keys_to_evaluate",
+ "description": "A list of specific flag keys to evaluate. If provided, only these flags will be evaluated, improving performance.",
+ "isOptional": true,
+ "type": "list[str]"
+ },
+ {
+ "name": "device_id",
+ "description": "The device ID for this request.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "FlagsResponse"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "decision = posthog.get_flags_decision('user123')"
+ }
+ ]
+ },
+ {
+ "id": "get_remote_config_payload",
+ "title": "get_remote_config_payload",
+ "description": "Get the payload for a remote config feature flag.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "key",
+ "description": "The remote config feature flag key.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ }
+ },
+ {
+ "id": "group_identify",
+ "title": "group_identify",
+ "description": "Identify a group and set its properties.",
+ "details": "",
+ "category": "Identification",
+ "params": [
+ {
+ "name": "group_type",
+ "description": "The type of group (e.g., 'company', 'team').",
+ "isOptional": true,
+ "type": "str"
+ },
+ {
+ "name": "group_key",
+ "description": "The unique identifier for the group.",
+ "isOptional": true,
+ "type": "str"
+ },
+ {
+ "name": "properties",
+ "description": "A dictionary of properties to set on the group.",
+ "isOptional": true,
+ "type": "dict[str, Any]"
+ },
+ {
+ "name": "timestamp",
+ "description": "The timestamp of the event.",
+ "isOptional": false,
+ "type": "datetime"
+ },
+ {
+ "name": "uuid",
+ "description": "A unique identifier for the event.",
+ "isOptional": true,
+ "type": "str"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this event.",
+ "isOptional": true,
+ "type": "bool"
+ },
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID of the user performing the action.",
+ "isOptional": false,
+ "type": "Number"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[str]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "posthog.group_identify('company', 'company_id_in_your_db', {\n 'name': 'Awesome Inc.',\n 'employees': 11\n})"
+ }
+ ]
+ },
+ {
+ "id": "join",
+ "title": "join",
+ "description": "End the consumer thread once the queue is empty. Do not use directly, call `shutdown()` instead.",
+ "details": "",
+ "category": null,
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "posthog.join()"
+ }
+ ]
+ },
+ {
+ "id": "load_feature_flags",
+ "title": "load_feature_flags",
+ "description": "Load feature flags for local evaluation.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "posthog.load_feature_flags()"
+ }
+ ]
+ },
+ {
+ "id": "new_context",
+ "title": "new_context",
+ "description": "Create a new context for managing shared state. Learn more about [contexts](/docs/libraries/python#contexts).",
+ "details": "",
+ "category": "Contexts",
+ "params": [
+ {
+ "name": "fresh",
+ "description": "Whether to create a fresh context that doesn't inherit from parent.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "capture_exceptions",
+ "description": "Whether to automatically capture exceptions in this context.",
+ "isOptional": false,
+ "type": "bool"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "with posthog.new_context():\n identify_context('')\n posthog.capture('event_name')"
+ }
+ ]
+ },
+ {
+ "id": "set",
+ "title": "set",
+ "description": "Set properties on a person profile.",
+ "details": "",
+ "category": "Identification",
+ "params": [
+ {
+ "name": "kwargs",
+ "description": "",
+ "isOptional": true,
+ "type": "Unpack[OptionalSetArgs]"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[str]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Set with distinct id",
+ "code": "# Set with distinct id\nposthog.set(distinct_id='user123', properties={'name': 'Max Hedgehog'})"
+ }
+ ]
+ },
+ {
+ "id": "set_once",
+ "title": "set_once",
+ "description": "Set properties on a person profile only if they haven't been set before.",
+ "details": "",
+ "category": "Identification",
+ "params": [
+ {
+ "name": "kwargs",
+ "description": "",
+ "isOptional": true,
+ "type": "Unpack[OptionalSetArgs]"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[str]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "posthog.set_once(distinct_id='user123', properties={'initial_signup_date': '2024-01-01'})"
+ }
+ ]
+ },
+ {
+ "id": "shutdown",
+ "title": "shutdown",
+ "description": "Flush all messages and cleanly shutdown the client. Call this before the process ends in serverless environments to avoid data loss.",
+ "details": "",
+ "category": null,
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "posthog.shutdown()"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "id": "PostHogModule",
+ "title": "PostHog Module Functions",
+ "description": "Global functions available in the PostHog module",
+ "functions": [
+ {
+ "id": "alias",
+ "title": "alias",
+ "description": "Associate user behaviour before and after they e.g. register, login, or perform some other identifying action.",
+ "details": "To marry up whatever a user does before they sign up or log in with what they do after you need to make an alias call. This will allow you to answer questions like \"Which marketing channels leads to users churning after a month?\" or \"What do users do on our website before signing up?\". Particularly useful for associating user behaviour before and after they e.g. register, login, or perform some other identifying action.",
+ "category": "Identification",
+ "params": [
+ {
+ "name": "previous_id",
+ "description": "The unique ID of the user before",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "distinct_id",
+ "description": "The current unique id",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "timestamp",
+ "description": "Optional timestamp for the event",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "uuid",
+ "description": "Optional UUID for the event",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP lookup",
+ "isOptional": false,
+ "type": "any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Alias user",
+ "code": "# Alias user\nfrom posthog import alias\nalias(previous_id='distinct_id', distinct_id='alias_id')"
+ }
+ ]
+ },
+ {
+ "id": "capture",
+ "title": "capture",
+ "description": "Capture anything a user does within your system.",
+ "details": "Capture allows you to capture anything a user does within your system, which you can later use in PostHog to find patterns in usage, work out which features to improve or where people are giving up. A capture call requires an event name to specify the event. We recommend using [verb] [noun], like `movie played` or `movie updated` to easily identify what your events mean later on. Capture takes a number of optional arguments, which are defined by the `OptionalCaptureArgs` type.",
+ "category": "Events",
+ "params": [
+ {
+ "name": "event",
+ "description": "The event name to specify the event **kwargs: Optional arguments including:",
+ "isOptional": true,
+ "type": "str"
+ },
+ {
+ "name": "kwargs",
+ "description": "",
+ "isOptional": true,
+ "type": "Unpack[OptionalCaptureArgs]"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[str]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Context and capture usage",
+ "code": "# Context and capture usage\nfrom posthog import new_context, identify_context, tag_context, capture\n# Enter a new context (e.g. a request/response cycle, an instance of a background job, etc)\nwith new_context():\n # Associate this context with some user, by distinct_id\n identify_context('some user')\n\n # Capture an event, associated with the context-level distinct ID ('some user')\n capture('movie started')\n\n # Capture an event associated with some other user (overriding the context-level distinct ID)\n capture('movie joined', distinct_id='some-other-user')\n\n # Capture an event with some properties\n capture('movie played', properties={'movie_id': '123', 'category': 'romcom'})\n\n # Capture an event with some properties\n capture('purchase', properties={'product_id': '123', 'category': 'romcom'})\n # Capture an event with some associated group\n capture('purchase', groups={'company': 'id:5'})\n\n # Adding a tag to the current context will cause it to appear on all subsequent events\n tag_context('some-tag', 'some-value')\n\n capture('another-event') # Will be captured with `'some-tag': 'some-value'` in the properties dict"
+ },
+ {
+ "id": "example_2",
+ "name": "Set event properties",
+ "code": "# Set event properties\nfrom posthog import capture\ncapture(\n \"user_signed_up\",\n distinct_id=\"distinct_id_of_the_user\",\n properties={\n \"login_type\": \"email\",\n \"is_free_trial\": \"true\"\n }\n)"
+ }
+ ]
+ },
+ {
+ "id": "capture_exception",
+ "title": "capture_exception",
+ "description": "Capture exceptions that happen in your code.",
+ "details": "Capture exception is idempotent - if it is called twice with the same exception instance, only a occurrence will be tracked in posthog. This is because, generally, contexts will cause exceptions to be captured automatically. However, to ensure you track an exception, if you catch and do not re-raise it, capturing it manually is recommended, unless you are certain it will have crossed a context boundary (e.g. by existing a `with posthog.new_context():` block already). If the passed exception was raised and caught, the captured stack trace will consist of every frame between where the exception was raised and the point at which it is captured (the \"traceback\"). If the passed exception was never raised, e.g. if you call `posthog.capture_exception(ValueError(\"Some Error\"))`, the stack trace captured will be the full stack trace at the moment the exception was captured. Note that heavy use of contexts will lead to truncated stack traces, as the exception will be captured by the context entered most recently, which may not be the point you catch the exception for the final time in your code. It's recommended to use contexts sparingly, for this reason. `capture_exception` takes the same set of optional arguments as `capture`.",
+ "category": "Events",
+ "params": [
+ {
+ "name": "exception",
+ "description": "The exception to capture. If not provided, the current exception is captured via `sys.exc_info()` **kwargs: Optional capture arguments including distinct_id, properties, timestamp, uuid, groups, flags, send_feature_flags, and disable_geoip.",
+ "isOptional": false,
+ "type": "BaseException"
+ },
+ {
+ "name": "kwargs",
+ "description": "",
+ "isOptional": true,
+ "type": "Unpack[OptionalCaptureArgs]"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Capture exception",
+ "code": "# Capture exception\nfrom posthog import capture_exception\ntry:\n risky_operation()\nexcept Exception as e:\n capture_exception(e)"
+ }
+ ]
+ },
+ {
+ "id": "evaluate_flags",
+ "title": "evaluate_flags",
+ "description": "Evaluate all feature flags for a user in a single call and return a :class:`FeatureFlagEvaluations` snapshot. Branch on ``.is_enabled()`` / ``.get_flag()`` and pass the same snapshot to ``capture()`` via the ``flags`` option so events carry the exact flag values the code branched on. Prefer this over repeated ``get_feature_flag()`` calls and over ``capture(send_feature_flags=True)`` \u2014 it consolidates flag evaluation into a single ``/flags`` request per incoming request.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "distinct_id",
+ "description": "The user's distinct ID. If ``None``, falls back to the context distinct_id. If still unresolvable, returns an empty snapshot.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "Mapping of group type to group key.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "Person properties to use for evaluation.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "Group properties keyed by group type.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "If ``True``, never fall back to remote evaluation.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP lookup.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "flag_keys",
+ "description": "Optional list of flag keys. When provided, only these flags are evaluated \u2014 the underlying ``/flags`` request asks the server for just this subset, which makes the response smaller and the request cheaper. Use this when you only need a handful of flags out of many.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "device_id",
+ "description": "Optional device ID override. If not provided, falls back to the context device_id (which may be set via tracing headers). Used by experience-continuity flags to match users across distinct_id changes.",
+ "isOptional": false,
+ "type": "any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "FeatureFlagEvaluations"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import evaluate_flags, capture\nflags = evaluate_flags(\"user_123\", person_properties={\"plan\": \"enterprise\"})\nif flags.is_enabled(\"new-dashboard\"):\n render_new_dashboard()\ncapture(\"page_viewed\", distinct_id=\"user_123\", flags=flags)"
+ }
+ ]
+ },
+ {
+ "id": "feature_enabled",
+ "title": "feature_enabled",
+ "description": "Use feature flags to enable or disable features for users.",
+ "details": "You can call `posthog.load_feature_flags()` before to make sure you're not doing unexpected requests.",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "key",
+ "description": "The feature flag key",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "distinct_id",
+ "description": "The user's distinct ID",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "Groups mapping",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "Person properties",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "Group properties",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to evaluate only locally",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "send_feature_flag_events",
+ "description": "Whether to send feature flag events",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP lookup",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "device_id",
+ "description": "Optional device ID override for experience-continuity flags",
+ "isOptional": false,
+ "type": "any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Boolean feature flag",
+ "code": "# Boolean feature flag\nfrom posthog import feature_enabled, get_feature_flag_payload\nis_my_flag_enabled = feature_enabled('flag-key', 'distinct_id_of_your_user')\nif is_my_flag_enabled:\n matched_flag_payload = get_feature_flag_payload('flag-key', 'distinct_id_of_your_user')"
+ }
+ ]
+ },
+ {
+ "id": "feature_flag_definitions",
+ "title": "feature_flag_definitions",
+ "description": "Returns loaded feature flags.",
+ "details": "Returns loaded feature flags, if any. Helpful for debugging what flag information you have loaded.",
+ "category": "Feature flags",
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import feature_flag_definitions\ndefinitions = feature_flag_definitions()"
+ }
+ ]
+ },
+ {
+ "id": "flush",
+ "title": "flush",
+ "description": "Tell the client to flush all queued events.",
+ "details": "",
+ "category": "Client management",
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import flush\nflush()"
+ }
+ ]
+ },
+ {
+ "id": "get_all_flags",
+ "title": "get_all_flags",
+ "description": "Get all flags for a given user.",
+ "details": "Flags are key-value pairs where the key is the flag key and the value is the flag variant, or True, or False.",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "distinct_id",
+ "description": "The user's distinct ID",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "Groups mapping",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "Person properties",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "Group properties",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to evaluate only locally",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP lookup",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "device_id",
+ "description": "Optional device ID override for experience-continuity flags",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "flag_keys_to_evaluate",
+ "description": "Optional list of flag keys to evaluate (evaluates all if None)",
+ "isOptional": false,
+ "type": "any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[dict[str, FeatureFlag]]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "All flags for user",
+ "code": "# All flags for user\nfrom posthog import get_all_flags\nget_all_flags('distinct_id_of_your_user')"
+ }
+ ]
+ },
+ {
+ "id": "get_all_flags_and_payloads",
+ "title": "get_all_flags_and_payloads",
+ "description": "Get all feature flag values and payloads for a user.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "distinct_id",
+ "description": "The user's distinct ID.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "Mapping of group type to group key.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "Person properties to use for evaluation.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "Group properties keyed by group type.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to evaluate only locally.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP lookup.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "device_id",
+ "description": "Optional device ID override for experience-continuity flags.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "flag_keys_to_evaluate",
+ "description": "Optional list of flag keys to evaluate. Evaluates all flags when omitted.",
+ "isOptional": false,
+ "type": "any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "FlagsAndPayloads"
+ }
+ },
+ {
+ "id": "get_feature_flag",
+ "title": "get_feature_flag",
+ "description": "Get feature flag variant for users. Used with experiments.",
+ "details": "`groups` are a mapping from group type to group key. So, if you have a group type of \"organization\" and a group key of \"5\", you would pass groups={\"organization\": \"5\"}. `group_properties` take the format: { group_type_name: { group_properties } }. So, for example, if you have the group type \"organization\" and the group key \"5\", with the properties name, and employee count, you'll send these as: group_properties={\"organization\": {\"name\": \"PostHog\", \"employees\": 11}}.",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "key",
+ "description": "The feature flag key",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "distinct_id",
+ "description": "The user's distinct ID",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "Groups mapping from group type to group key",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "Person properties",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "Group properties in format { group_type_name: { group_properties } }",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to evaluate only locally",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "send_feature_flag_events",
+ "description": "Whether to send feature flag events",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP lookup",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "device_id",
+ "description": "Optional device ID override for experience-continuity flags",
+ "isOptional": false,
+ "type": "any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[FeatureFlag]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Multivariate feature flag",
+ "code": "# Multivariate feature flag\nfrom posthog import get_feature_flag, get_feature_flag_payload\nenabled_variant = get_feature_flag('flag-key', 'distinct_id_of_your_user')\nif enabled_variant == 'variant-key':\n matched_flag_payload = get_feature_flag_payload('flag-key', 'distinct_id_of_your_user')"
+ }
+ ]
+ },
+ {
+ "id": "get_feature_flag_payload",
+ "title": "get_feature_flag_payload",
+ "description": "Get the payload associated with a feature flag value. Deprecated for new code. Prefer ``evaluate_flags()`` and ``flags.get_flag_payload(key)`` so flag evaluation happens once per request.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "key",
+ "description": "The feature flag key.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "distinct_id",
+ "description": "The user's distinct ID.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "match_value",
+ "description": "Optional flag value to use when selecting a payload.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "Mapping of group type to group key.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "Person properties to use for evaluation.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "Group properties keyed by group type.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to evaluate only locally.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "send_feature_flag_events",
+ "description": "Whether to send a $feature_flag_called event.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP lookup.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "device_id",
+ "description": "Optional device ID override for experience-continuity flags.",
+ "isOptional": false,
+ "type": "any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[str]"
+ }
+ },
+ {
+ "id": "get_feature_flag_result",
+ "title": "get_feature_flag_result",
+ "description": "Get a FeatureFlagResult object which contains the flag result and payload. This method evaluates a feature flag and returns a FeatureFlagResult object containing: - enabled: Whether the flag is enabled - variant: The variant value if the flag has variants - payload: The payload associated with the flag (automatically deserialized from JSON) - key: The flag key - reason: Why the flag was enabled/disabled",
+ "details": "",
+ "category": null,
+ "params": [
+ {
+ "name": "key",
+ "description": "The feature flag key.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "distinct_id",
+ "description": "The user's distinct ID.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "Mapping of group type to group key.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "Person properties to use for evaluation.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "Group properties keyed by group type.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to evaluate only locally.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "send_feature_flag_events",
+ "description": "Whether to send a $feature_flag_called event.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP lookup.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "device_id",
+ "description": "Optional device ID override for experience-continuity flags.",
+ "isOptional": false,
+ "type": "any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ }
+ },
+ {
+ "id": "get_remote_config_payload",
+ "title": "get_remote_config_payload",
+ "description": "Get the payload for a remote config feature flag.",
+ "details": "",
+ "category": null,
+ "params": [
+ {
+ "name": "key",
+ "description": "The key of the feature flag",
+ "isOptional": true,
+ "type": "any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ }
+ },
+ {
+ "id": "get_tags",
+ "title": "get_tags",
+ "description": "Get all tags from the current context. Returns: Dict of all tags in the current context",
+ "details": "",
+ "category": "Contexts",
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "dict[str, Any]"
+ }
+ },
+ {
+ "id": "group_identify",
+ "title": "group_identify",
+ "description": "Set properties on a group.",
+ "details": "",
+ "category": "Identification",
+ "params": [
+ {
+ "name": "group_type",
+ "description": "Type of your group",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "group_key",
+ "description": "Unique identifier of the group",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "properties",
+ "description": "Properties to set on the group",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "timestamp",
+ "description": "Optional timestamp for the event",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "uuid",
+ "description": "Optional UUID for the event",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP lookup",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "distinct_id",
+ "description": "Optional distinct ID of the user performing the action",
+ "isOptional": false,
+ "type": "any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Group identify",
+ "code": "# Group identify\nfrom posthog import group_identify\ngroup_identify('company', 'company_id_in_your_db', {\n 'name': 'Awesome Inc.',\n 'employees': 11\n})"
+ }
+ ]
+ },
+ {
+ "id": "identify_context",
+ "title": "identify_context",
+ "description": "Identify the current context with a distinct ID.",
+ "details": "",
+ "category": "Identification",
+ "params": [
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID to associate with the current context and its children",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import identify_context\nidentify_context(\"user_123\")"
+ }
+ ]
+ },
+ {
+ "id": "join",
+ "title": "join",
+ "description": "Block program until the client clears the queue. Used during program shutdown. You should use `shutdown()` directly in most cases.",
+ "details": "",
+ "category": "Client management",
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import join\njoin()"
+ }
+ ]
+ },
+ {
+ "id": "load_feature_flags",
+ "title": "load_feature_flags",
+ "description": "Load feature flag definitions from PostHog.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import load_feature_flags\nload_feature_flags()"
+ }
+ ]
+ },
+ {
+ "id": "new_context",
+ "title": "new_context",
+ "description": "Create a new context scope that will be active for the duration of the with block.",
+ "details": "",
+ "category": "Contexts",
+ "params": [
+ {
+ "name": "fresh",
+ "description": "Whether to start with a fresh context (default: False)",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "capture_exceptions",
+ "description": "Whether to capture exceptions raised within the context (default: True)",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "client",
+ "description": "Optional Posthog client instance to use for this context (default: None)",
+ "isOptional": true,
+ "type": "Client"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import new_context, tag, capture\nwith new_context():\n tag(\"request_id\", \"123\")\n capture(\"event_name\", properties={\"property\": \"value\"})"
+ }
+ ]
+ },
+ {
+ "id": "scoped",
+ "title": "scoped",
+ "description": "Decorator that creates a new context for the function.",
+ "details": "",
+ "category": "Contexts",
+ "params": [
+ {
+ "name": "fresh",
+ "description": "Whether to start with a fresh context (default: False)",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "capture_exceptions",
+ "description": "Whether to capture and track exceptions with posthog error tracking (default: True)",
+ "isOptional": false,
+ "type": "bool"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import scoped, tag, capture\n@scoped()\ndef process_payment(payment_id):\n tag(\"payment_id\", payment_id)\n capture(\"payment_started\")"
+ }
+ ]
+ },
+ {
+ "id": "set",
+ "title": "set",
+ "description": "Set properties on a user record.",
+ "details": "This will overwrite previous people property values. Generally operates similar to `capture`, with distinct_id being an optional argument, defaulting to the current context's distinct ID. If there is no context-level distinct ID, and no override distinct_id is passed, this function will do nothing. Context tags are folded into $set properties, so tagging the current context and then calling `set` will cause those tags to be set on the user (unlike capture, which causes them to just be set on the event).",
+ "category": "Identification",
+ "params": [
+ {
+ "name": "kwargs",
+ "description": "",
+ "isOptional": true,
+ "type": "Unpack[OptionalSetArgs]"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[str]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Set person properties",
+ "code": "# Set person properties\nfrom posthog import set\nset(distinct_id='distinct_id', properties={'name': 'Max Hedgehog'})"
+ }
+ ]
+ },
+ {
+ "id": "set_capture_exception_code_variables_context",
+ "title": "set_capture_exception_code_variables_context",
+ "description": "Override code-variable capture for exceptions in the current context.",
+ "details": "",
+ "category": "Contexts",
+ "params": [
+ {
+ "name": "enabled",
+ "description": "Whether exceptions captured in this context should include local variable values from stack frames.",
+ "isOptional": true,
+ "type": "bool"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ }
+ },
+ {
+ "id": "set_code_variables_ignore_patterns_context",
+ "title": "set_code_variables_ignore_patterns_context",
+ "description": "Override code-variable ignore patterns for exceptions in the current context.",
+ "details": "",
+ "category": "Contexts",
+ "params": [
+ {
+ "name": "ignore_patterns",
+ "description": "Variable-name patterns that should be omitted entirely when code variables are captured.",
+ "isOptional": true,
+ "type": "list"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ }
+ },
+ {
+ "id": "set_code_variables_mask_patterns_context",
+ "title": "set_code_variables_mask_patterns_context",
+ "description": "Override code-variable mask patterns for exceptions in the current context.",
+ "details": "",
+ "category": "Contexts",
+ "params": [
+ {
+ "name": "mask_patterns",
+ "description": "Variable-name patterns whose values should be replaced with ``***`` when code variables are captured.",
+ "isOptional": true,
+ "type": "list"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ }
+ },
+ {
+ "id": "set_context_device_id",
+ "title": "set_context_device_id",
+ "description": "Set the device ID for the current context, associating all feature flag requests in this or child contexts with the given device ID.",
+ "details": "",
+ "category": "Contexts",
+ "params": [
+ {
+ "name": "device_id",
+ "description": "The device ID to associate with the current context and its children",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import set_context_device_id\nset_context_device_id(\"device_123\")"
+ }
+ ]
+ },
+ {
+ "id": "set_context_session",
+ "title": "set_context_session",
+ "description": "Set the session ID for the current context.",
+ "details": "",
+ "category": "Contexts",
+ "params": [
+ {
+ "name": "session_id",
+ "description": "The session ID to associate with the current context and its children",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import set_context_session\nset_context_session(\"session_123\")"
+ }
+ ]
+ },
+ {
+ "id": "set_once",
+ "title": "set_once",
+ "description": "Set properties on a user record, only if they do not yet exist.",
+ "details": "This will not overwrite previous people property values, unlike `set`. Otherwise, operates in an identical manner to `set`.",
+ "category": "Identification",
+ "params": [
+ {
+ "name": "kwargs",
+ "description": "",
+ "isOptional": true,
+ "type": "Unpack[OptionalSetArgs]"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[str]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Set property once",
+ "code": "# Set property once\nfrom posthog import set_once\nset_once(distinct_id='distinct_id', properties={'initial_url': '/blog'})"
+ }
+ ]
+ },
+ {
+ "id": "setup",
+ "title": "setup",
+ "description": "Create or return the global PostHog client configured by module settings. Most applications should either instantiate ``Posthog`` directly or set ``posthog.api_key``/other module settings before calling top-level helpers. ``setup()`` is called automatically by global APIs such as ``capture()``. Returns: The global ``Client`` instance. If ``api_key`` is missing or blank, the client is disabled and module-level calls become no-ops.",
+ "details": "",
+ "category": "Initialization",
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Client"
+ }
+ },
+ {
+ "id": "shutdown",
+ "title": "shutdown",
+ "description": "Flush all messages and cleanly shutdown the client.",
+ "details": "",
+ "category": "Client management",
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import shutdown\nshutdown()"
+ }
+ ]
+ },
+ {
+ "id": "tag",
+ "title": "tag",
+ "description": "Add a tag to the current context.",
+ "details": "",
+ "category": "Contexts",
+ "params": [
+ {
+ "name": "name",
+ "description": "The tag key",
+ "isOptional": true,
+ "type": "str"
+ },
+ {
+ "name": "value",
+ "description": "The tag value",
+ "isOptional": true,
+ "type": "Any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import tag\ntag(\"user_id\", \"123\")"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "categories": [
+ "Initialization",
+ "Identification",
+ "Capture",
+ "Error Tracking",
+ "Feature flags",
+ "Contexts",
+ "Events",
+ "Client management"
+ ]
+}
\ No newline at end of file
diff --git a/references/posthog-python-references-latest.json b/references/posthog-python-references-latest.json
index e3e8c526..efe2ea4b 100644
--- a/references/posthog-python-references-latest.json
+++ b/references/posthog-python-references-latest.json
@@ -2,7 +2,7 @@
"id": "posthog-python",
"hogRef": "0.3",
"info": {
- "version": "7.18.3",
+ "version": "7.19.0",
"id": "posthog-python",
"title": "PostHog Python SDK",
"description": "Integrate PostHog into any python application.",
@@ -550,6 +550,30 @@
"isOptional": false,
"type": "UnionType[list[str], any]"
},
+ {
+ "name": "enable_exception_autocapture_rate_limiting",
+ "description": "Rate limit autocaptured exceptions client-side with a token bucket per exception type. Disabled by default.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "exception_autocapture_bucket_size",
+ "description": "Maximum burst of autocaptured exceptions allowed per exception type (token bucket size, clamped to 0-100).",
+ "isOptional": false,
+ "type": "int"
+ },
+ {
+ "name": "exception_autocapture_refill_rate",
+ "description": "Tokens restored per refill interval for each exception type's bucket.",
+ "isOptional": false,
+ "type": "int"
+ },
+ {
+ "name": "exception_autocapture_refill_interval_seconds",
+ "description": "Seconds between token refills for autocaptured exception rate limiting.",
+ "isOptional": false,
+ "type": "int"
+ },
{
"name": "_dedicated_ai_endpoint",
"description": "",
From 8d416aef20f12c0c8c405baf7c7d08f3352f3632 Mon Sep 17 00:00:00 2001
From: Mia Miu
Date: Mon, 15 Jun 2026 22:42:32 +1200
Subject: [PATCH 4/8] fix: add missing return type annotations (#640)
* fix: add missing return type annotations
* chore: add changeset for missing return type annotations
---------
Co-authored-by: Manoel Aranda Neto
---
.sampo/changesets/missing-return-type-annotations.md | 5 +++++
posthog/__init__.py | 8 ++++----
posthog/client.py | 9 +++++----
3 files changed, 14 insertions(+), 8 deletions(-)
create mode 100644 .sampo/changesets/missing-return-type-annotations.md
diff --git a/.sampo/changesets/missing-return-type-annotations.md b/.sampo/changesets/missing-return-type-annotations.md
new file mode 100644
index 00000000..5d5880fa
--- /dev/null
+++ b/.sampo/changesets/missing-return-type-annotations.md
@@ -0,0 +1,5 @@
+---
+pypi/posthog: patch
+---
+
+Add missing return type annotations to improve typing coverage without changing runtime behavior.
diff --git a/posthog/__init__.py b/posthog/__init__.py
index a8657b71..9f0b4893 100644
--- a/posthog/__init__.py
+++ b/posthog/__init__.py
@@ -581,7 +581,7 @@ def alias(
def capture_exception(
exception: Optional[ExceptionArg] = None,
**kwargs: Unpack[OptionalCaptureArgs],
-):
+) -> Optional[str]:
"""
Capture exceptions that happen in your code.
@@ -1026,7 +1026,7 @@ def load_feature_flags():
return _proxy("load_feature_flags")
-def flush():
+def flush() -> None:
"""
Tell the client to flush all queued events.
@@ -1042,7 +1042,7 @@ def flush():
_proxy("flush")
-def join():
+def join() -> None:
"""
Block program until the client clears the queue. Used during program shutdown. You should use `shutdown()` directly in most cases.
@@ -1058,7 +1058,7 @@ def join():
_proxy("join")
-def shutdown():
+def shutdown() -> None:
"""
Flush all messages and cleanly shutdown the client.
diff --git a/posthog/client.py b/posthog/client.py
index 6fc4481b..20d1a9f9 100644
--- a/posthog/client.py
+++ b/posthog/client.py
@@ -1139,7 +1139,7 @@ def capture_exception(
self,
exception: Optional[ExceptionArg],
**kwargs: Unpack[OptionalCaptureArgs],
- ):
+ ) -> Optional[str]:
"""
Capture an exception for error tracking.
@@ -1261,6 +1261,7 @@ def capture_exception(
return res
except Exception as e:
self.log.exception(f"Failed to capture exception: {e}")
+ return None
@staticmethod
def _reinit_after_fork_weak(weak_self):
@@ -1423,7 +1424,7 @@ def _enqueue(self, msg, disable_geoip):
self.log.warning("analytics-python queue is full")
return None
- def flush(self):
+ def flush(self) -> None:
"""
Force a flush from the internal queue to the server. Do not use directly, call `shutdown()` instead.
@@ -1439,7 +1440,7 @@ def flush(self):
# Note that this message may not be precise, because of threading.
self.log.debug("successfully flushed about %s items.", size)
- def join(self):
+ def join(self) -> None:
"""
End the consumer thread once the queue is empty. Do not use directly, call `shutdown()` instead.
@@ -1463,7 +1464,7 @@ def join(self):
# Shutdown the cache provider (release locks, cleanup)
self._shutdown_flag_definition_cache_provider()
- def shutdown(self):
+ def shutdown(self) -> None:
"""
Flush all messages and cleanly shutdown the client. Call this before the process ends in serverless environments to avoid data loss.
From 7f503491c75e188f1eb9c2dcefb599995173ccbd Mon Sep 17 00:00:00 2001
From: "releaser-posthog-python[bot]"
<262414804+releaser-posthog-python[bot]@users.noreply.github.com>
Date: Mon, 15 Jun 2026 11:23:22 +0000
Subject: [PATCH 5/8] chore: Release v7.19.1 [skip ci]
---
.sampo/changesets/missing-return-type-annotations.md | 5 -----
CHANGELOG.md | 6 ++++++
posthog/version.py | 2 +-
pyproject.toml | 2 +-
uv.lock | 2 +-
5 files changed, 9 insertions(+), 8 deletions(-)
delete mode 100644 .sampo/changesets/missing-return-type-annotations.md
diff --git a/.sampo/changesets/missing-return-type-annotations.md b/.sampo/changesets/missing-return-type-annotations.md
deleted file mode 100644
index 5d5880fa..00000000
--- a/.sampo/changesets/missing-return-type-annotations.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-pypi/posthog: patch
----
-
-Add missing return type annotations to improve typing coverage without changing runtime behavior.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9bfbf832..f91a6295 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# posthog
+## 7.19.1 — 2026-06-15
+
+### Patch changes
+
+- [8d416ae](https://github.com/posthog/posthog-python/commit/8d416aef20f12c0c8c405baf7c7d08f3352f3632) Add missing return type annotations to improve typing coverage without changing runtime behavior. — Thanks @miachillgood for your first contribution 🎉!
+
## 7.19.0 — 2026-06-15
### Minor changes
diff --git a/posthog/version.py b/posthog/version.py
index 69f0099d..ec5d14c3 100644
--- a/posthog/version.py
+++ b/posthog/version.py
@@ -1 +1 @@
-VERSION = "7.19.0"
+VERSION = "7.19.1"
diff --git a/pyproject.toml b/pyproject.toml
index 2998c88e..2a46eba0 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "posthog"
-version = "7.19.0"
+version = "7.19.1"
description = "Integrate PostHog into any python application."
authors = [{ name = "PostHog", email = "engineering@posthog.com" }]
maintainers = [{ name = "PostHog", email = "engineering@posthog.com" }]
diff --git a/uv.lock b/uv.lock
index 8ee98040..1a7598c5 100644
--- a/uv.lock
+++ b/uv.lock
@@ -2169,7 +2169,7 @@ wheels = [
[[package]]
name = "posthog"
-version = "7.19.0"
+version = "7.19.1"
source = { editable = "." }
dependencies = [
{ name = "backoff" },
From bd7079947e44212a813fdcba4a588bed888bb320 Mon Sep 17 00:00:00 2001
From: "releaser-posthog-python[bot]"
<262414804+releaser-posthog-python[bot]@users.noreply.github.com>
Date: Mon, 15 Jun 2026 11:23:56 +0000
Subject: [PATCH 6/8] Update generated references
---
.../posthog-python-references-7.19.1.json | 2948 +++++++++++++++++
.../posthog-python-references-latest.json | 18 +-
2 files changed, 2957 insertions(+), 9 deletions(-)
create mode 100644 references/posthog-python-references-7.19.1.json
diff --git a/references/posthog-python-references-7.19.1.json b/references/posthog-python-references-7.19.1.json
new file mode 100644
index 00000000..153a5a63
--- /dev/null
+++ b/references/posthog-python-references-7.19.1.json
@@ -0,0 +1,2948 @@
+{
+ "id": "posthog-python",
+ "hogRef": "0.3",
+ "info": {
+ "version": "7.19.1",
+ "id": "posthog-python",
+ "title": "PostHog Python SDK",
+ "description": "Integrate PostHog into any python application.",
+ "slugPrefix": "posthog-python",
+ "specUrl": "https://github.com/PostHog/posthog-python"
+ },
+ "types": [
+ {
+ "id": "FeatureFlag",
+ "name": "FeatureFlag",
+ "path": "posthog.types.FeatureFlag",
+ "properties": [
+ {
+ "name": "key",
+ "type": "str",
+ "description": "Field: key"
+ },
+ {
+ "name": "enabled",
+ "type": "bool",
+ "description": "Field: enabled"
+ },
+ {
+ "name": "variant",
+ "type": "Optional[str]",
+ "description": "Field: variant"
+ },
+ {
+ "name": "reason",
+ "type": "Optional[FlagReason]",
+ "description": "Field: reason"
+ },
+ {
+ "name": "metadata",
+ "type": "Union[FlagMetadata, LegacyFlagMetadata]",
+ "description": "Field: metadata"
+ }
+ ],
+ "example": ""
+ },
+ {
+ "id": "FeatureFlagResult",
+ "name": "FeatureFlagResult",
+ "path": "posthog.types.FeatureFlagResult",
+ "properties": [
+ {
+ "name": "key",
+ "type": "str",
+ "description": "Field: key"
+ },
+ {
+ "name": "enabled",
+ "type": "bool",
+ "description": "Field: enabled"
+ },
+ {
+ "name": "variant",
+ "type": "Optional[str]",
+ "description": "Field: variant"
+ },
+ {
+ "name": "payload",
+ "type": "Optional[Any]",
+ "description": "Field: payload"
+ },
+ {
+ "name": "reason",
+ "type": "Optional[str]",
+ "description": "Field: reason"
+ }
+ ],
+ "example": ""
+ },
+ {
+ "id": "FlagMetadata",
+ "name": "FlagMetadata",
+ "path": "posthog.types.FlagMetadata",
+ "properties": [
+ {
+ "name": "id",
+ "type": "int",
+ "description": "Field: id"
+ },
+ {
+ "name": "payload",
+ "type": "Optional[str]",
+ "description": "Field: payload"
+ },
+ {
+ "name": "version",
+ "type": "int",
+ "description": "Field: version"
+ },
+ {
+ "name": "description",
+ "type": "str",
+ "description": "Field: description"
+ }
+ ],
+ "example": ""
+ },
+ {
+ "id": "FlagReason",
+ "name": "FlagReason",
+ "path": "posthog.types.FlagReason",
+ "properties": [
+ {
+ "name": "code",
+ "type": "str",
+ "description": "Field: code"
+ },
+ {
+ "name": "condition_index",
+ "type": "Optional[int]",
+ "description": "Field: condition_index"
+ },
+ {
+ "name": "description",
+ "type": "str",
+ "description": "Field: description"
+ }
+ ],
+ "example": ""
+ },
+ {
+ "id": "FlagsAndPayloads",
+ "name": "FlagsAndPayloads",
+ "path": "posthog.types.FlagsAndPayloads",
+ "properties": [
+ {
+ "name": "featureFlags",
+ "type": "Optional[dict[str, Union[bool, str]]]",
+ "description": "Field: featureFlags"
+ },
+ {
+ "name": "featureFlagPayloads",
+ "type": "Optional[dict[str, Any]]",
+ "description": "Field: featureFlagPayloads"
+ }
+ ],
+ "example": ""
+ },
+ {
+ "id": "FlagsResponse",
+ "name": "FlagsResponse",
+ "path": "posthog.types.FlagsResponse",
+ "properties": [
+ {
+ "name": "flags",
+ "type": "dict[str, FeatureFlag]",
+ "description": "Field: flags"
+ },
+ {
+ "name": "errorsWhileComputingFlags",
+ "type": "bool",
+ "description": "Field: errorsWhileComputingFlags"
+ },
+ {
+ "name": "requestId",
+ "type": "str",
+ "description": "Field: requestId"
+ },
+ {
+ "name": "quotaLimit",
+ "type": "Optional[list[str]]",
+ "description": "Field: quotaLimit"
+ },
+ {
+ "name": "evaluatedAt",
+ "type": "Optional[int]",
+ "description": "Field: evaluatedAt"
+ }
+ ],
+ "example": ""
+ },
+ {
+ "id": "LegacyFlagMetadata",
+ "name": "LegacyFlagMetadata",
+ "path": "posthog.types.LegacyFlagMetadata",
+ "properties": [
+ {
+ "name": "payload",
+ "type": "Any",
+ "description": "Field: payload"
+ }
+ ],
+ "example": ""
+ },
+ {
+ "id": "SendFeatureFlagsOptions",
+ "name": "SendFeatureFlagsOptions",
+ "path": "posthog.types.SendFeatureFlagsOptions",
+ "properties": [
+ {
+ "name": "should_send",
+ "type": "bool",
+ "description": "Field: should_send"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "type": "Optional[bool]",
+ "description": "Field: only_evaluate_locally"
+ },
+ {
+ "name": "person_properties",
+ "type": "Optional[dict[str, Any]]",
+ "description": "Field: person_properties"
+ },
+ {
+ "name": "group_properties",
+ "type": "Optional[dict[str, dict[str, Any]]]",
+ "description": "Field: group_properties"
+ },
+ {
+ "name": "flag_keys_filter",
+ "type": "Optional[list[str]]",
+ "description": "Field: flag_keys_filter"
+ }
+ ],
+ "example": ""
+ },
+ {
+ "id": "OptionalCaptureArgs",
+ "name": "OptionalCaptureArgs",
+ "path": "posthog.args.OptionalCaptureArgs",
+ "properties": [
+ {
+ "name": "distinct_id",
+ "type": "NotRequired[Union[Number, str, UUID, int, any]]",
+ "description": "Field: distinct_id"
+ },
+ {
+ "name": "properties",
+ "type": "NotRequired[Optional[dict[str, Any]]]",
+ "description": "Field: properties"
+ },
+ {
+ "name": "timestamp",
+ "type": "NotRequired[Union[datetime, str, any]]",
+ "description": "Field: timestamp"
+ },
+ {
+ "name": "uuid",
+ "type": "NotRequired[Optional[str]]",
+ "description": "Field: uuid"
+ },
+ {
+ "name": "groups",
+ "type": "NotRequired[Optional[dict[str, str]]]",
+ "description": "Field: groups"
+ },
+ {
+ "name": "flags",
+ "type": "NotRequired[Optional[ForwardRef('FeatureFlagEvaluations')]]",
+ "description": "Field: flags"
+ },
+ {
+ "name": "send_feature_flags",
+ "type": "NotRequired[Union[bool, SendFeatureFlagsOptions, any]]",
+ "description": "Field: send_feature_flags"
+ },
+ {
+ "name": "disable_geoip",
+ "type": "NotRequired[Optional[bool]]",
+ "description": "Field: disable_geoip"
+ }
+ ],
+ "example": ""
+ },
+ {
+ "id": "OptionalSetArgs",
+ "name": "OptionalSetArgs",
+ "path": "posthog.args.OptionalSetArgs",
+ "properties": [
+ {
+ "name": "distinct_id",
+ "type": "NotRequired[Union[Number, str, UUID, int, any]]",
+ "description": "Field: distinct_id"
+ },
+ {
+ "name": "properties",
+ "type": "NotRequired[Optional[dict[str, Any]]]",
+ "description": "Field: properties"
+ },
+ {
+ "name": "timestamp",
+ "type": "NotRequired[Union[datetime, str, any]]",
+ "description": "Field: timestamp"
+ },
+ {
+ "name": "uuid",
+ "type": "NotRequired[Optional[str]]",
+ "description": "Field: uuid"
+ },
+ {
+ "name": "disable_geoip",
+ "type": "NotRequired[Optional[bool]]",
+ "description": "Field: disable_geoip"
+ }
+ ],
+ "example": ""
+ },
+ {
+ "id": "SendFeatureFlagsOptions",
+ "name": "SendFeatureFlagsOptions",
+ "path": "posthog.types.SendFeatureFlagsOptions",
+ "properties": [
+ {
+ "name": "should_send",
+ "type": "bool",
+ "description": "Field: should_send"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "type": "Optional[bool]",
+ "description": "Field: only_evaluate_locally"
+ },
+ {
+ "name": "person_properties",
+ "type": "Optional[dict[str, Any]]",
+ "description": "Field: person_properties"
+ },
+ {
+ "name": "group_properties",
+ "type": "Optional[dict[str, dict[str, Any]]]",
+ "description": "Field: group_properties"
+ },
+ {
+ "name": "flag_keys_filter",
+ "type": "Optional[list[str]]",
+ "description": "Field: flag_keys_filter"
+ }
+ ],
+ "example": ""
+ }
+ ],
+ "classes": [
+ {
+ "id": "PostHog",
+ "title": "PostHog",
+ "description": "This is the SDK reference for the PostHog Python SDK. You can learn more about example usage in the [Python SDK documentation](/docs/libraries/python). You can also follow [Flask](/docs/libraries/flask) and [Django](/docs/libraries/django) guides to integrate PostHog into your project.",
+ "functions": [
+ {
+ "id": "__init__",
+ "title": "Client",
+ "description": "Initialize a new PostHog client instance.",
+ "details": "",
+ "category": "Initialization",
+ "params": [
+ {
+ "name": "project_api_key",
+ "description": "PostHog project API key/token.",
+ "isOptional": true,
+ "type": "str"
+ },
+ {
+ "name": "host",
+ "description": "PostHog host. Defaults to the US ingestion endpoint when not set. App hosts such as ``https://us.posthog.com`` are mapped to the corresponding ingestion host.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "debug",
+ "description": "Enable verbose SDK logging and re-raise errors from public API methods.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "max_queue_size",
+ "description": "Maximum number of events buffered before upload.",
+ "isOptional": false,
+ "type": "int"
+ },
+ {
+ "name": "send",
+ "description": "If False, queueing succeeds but events are not sent.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "on_error",
+ "description": "Optional callback invoked by background consumers when an upload fails.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "flush_at",
+ "description": "Number of queued events that triggers a batch upload.",
+ "isOptional": false,
+ "type": "int"
+ },
+ {
+ "name": "flush_interval",
+ "description": "Maximum seconds a background consumer waits before flushing a partial batch.",
+ "isOptional": false,
+ "type": "float"
+ },
+ {
+ "name": "gzip",
+ "description": "Whether to gzip event upload payloads.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "max_retries",
+ "description": "Number of upload retries for background consumers.",
+ "isOptional": false,
+ "type": "int"
+ },
+ {
+ "name": "sync_mode",
+ "description": "If True, send each event synchronously instead of using background worker threads.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "timeout",
+ "description": "HTTP request timeout in seconds for event uploads.",
+ "isOptional": false,
+ "type": "int"
+ },
+ {
+ "name": "thread",
+ "description": "Number of background consumer threads.",
+ "isOptional": false,
+ "type": "int"
+ },
+ {
+ "name": "poll_interval",
+ "description": "Seconds between local feature flag definition refreshes.",
+ "isOptional": false,
+ "type": "int"
+ },
+ {
+ "name": "personal_api_key",
+ "description": "Personal API key used for local feature flag evaluation and remote config payloads.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "disabled",
+ "description": "If True, disable captures and API requests. Useful in tests.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable server-side GeoIP enrichment. Defaults to True.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "is_server",
+ "description": "Whether events are emitted from a server-side runtime. Defaults to True; set to False when using the SDK as a client/CLI so the device OS is attributed to the person normally.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "historical_migration",
+ "description": "Mark events as historical migration imports.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "feature_flags_request_timeout_seconds",
+ "description": "Timeout in seconds for feature flag and remote config requests.",
+ "isOptional": false,
+ "type": "int"
+ },
+ {
+ "name": "super_properties",
+ "description": "Properties merged into every captured event.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "enable_exception_autocapture",
+ "description": "Automatically capture uncaught exceptions.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "log_captured_exceptions",
+ "description": "Also log exceptions captured by error tracking.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "project_root",
+ "description": "Root path used to determine in-app stack frames for captured exceptions. Defaults to the current working directory.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "privacy_mode",
+ "description": "For AI observability, capture usage metadata without prompt inputs or outputs.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "before_send",
+ "description": "Optional callback that can modify or drop events before upload. Return ``None`` to drop an event.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "flag_fallback_cache_url",
+ "description": "Optional feature flag fallback cache URL, such as ``memory://local/?ttl=300&size=10000`` or a Redis URL.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "enable_local_evaluation",
+ "description": "Whether to poll feature flag definitions for local evaluation when a personal API key is configured.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "flag_definition_cache_provider",
+ "description": "Optional external cache provider for sharing feature flag definitions across workers.",
+ "isOptional": true,
+ "type": "FlagDefinitionCacheProvider"
+ },
+ {
+ "name": "capture_exception_code_variables",
+ "description": "Capture local variable values on exception stack frames.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "code_variables_mask_patterns",
+ "description": "Variable-name patterns to mask when capturing code variables.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "code_variables_ignore_patterns",
+ "description": "Variable-name patterns to omit when capturing code variables.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "in_app_modules",
+ "description": "Module/package prefixes treated as in-app frames in captured exceptions.",
+ "isOptional": false,
+ "type": "UnionType[list[str], any]"
+ },
+ {
+ "name": "enable_exception_autocapture_rate_limiting",
+ "description": "Rate limit autocaptured exceptions client-side with a token bucket per exception type. Disabled by default.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "exception_autocapture_bucket_size",
+ "description": "Maximum burst of autocaptured exceptions allowed per exception type (token bucket size, clamped to 0-100).",
+ "isOptional": false,
+ "type": "int"
+ },
+ {
+ "name": "exception_autocapture_refill_rate",
+ "description": "Tokens restored per refill interval for each exception type's bucket.",
+ "isOptional": false,
+ "type": "int"
+ },
+ {
+ "name": "exception_autocapture_refill_interval_seconds",
+ "description": "Seconds between token refills for autocaptured exception rate limiting.",
+ "isOptional": false,
+ "type": "int"
+ },
+ {
+ "name": "_dedicated_ai_endpoint",
+ "description": "",
+ "isOptional": false,
+ "type": "bool"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import Posthog\n\nposthog = Posthog('', host='')"
+ }
+ ]
+ },
+ {
+ "id": "alias",
+ "title": "alias",
+ "description": "Create an alias between two distinct IDs.",
+ "details": "",
+ "category": "Identification",
+ "params": [
+ {
+ "name": "previous_id",
+ "description": "The previous distinct ID.",
+ "isOptional": true,
+ "type": "str"
+ },
+ {
+ "name": "distinct_id",
+ "description": "The new distinct ID to alias to.",
+ "isOptional": true,
+ "type": "str"
+ },
+ {
+ "name": "timestamp",
+ "description": "The timestamp of the event.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "uuid",
+ "description": "A unique identifier for the event.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this event.",
+ "isOptional": false,
+ "type": "any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "posthog.alias(previous_id='distinct_id', distinct_id='alias_id')"
+ }
+ ]
+ },
+ {
+ "id": "capture",
+ "title": "capture",
+ "description": "Captures an event manually. [Learn about capture best practices](https://posthog.com/docs/product-analytics/capture-events)",
+ "details": "",
+ "category": "Capture",
+ "params": [
+ {
+ "name": "event",
+ "description": "The event name to capture.",
+ "isOptional": true,
+ "type": "str"
+ },
+ {
+ "name": "kwargs",
+ "description": "",
+ "isOptional": true,
+ "type": "Unpack[OptionalCaptureArgs]"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[str]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Anonymous event",
+ "code": "# Anonymous event\nposthog.capture('some-anon-event')"
+ },
+ {
+ "id": "example_2",
+ "name": "Context usage",
+ "code": "# Context usage\nfrom posthog import identify_context, new_context\nwith new_context():\n identify_context('distinct_id_of_the_user')\n posthog.capture('user_signed_up')\n posthog.capture('user_logged_in')\n posthog.capture('some-custom-action', distinct_id='distinct_id_of_the_user')"
+ },
+ {
+ "id": "example_3",
+ "name": "Set event properties",
+ "code": "# Set event properties\nposthog.capture(\n \"user_signed_up\",\n distinct_id=\"distinct_id_of_the_user\",\n properties={\n \"login_type\": \"email\",\n \"is_free_trial\": \"true\"\n }\n)"
+ },
+ {
+ "id": "example_4",
+ "name": "Page view event",
+ "code": "# Page view event\nposthog.capture('$pageview', distinct_id=\"distinct_id_of_the_user\", properties={'$current_url': 'https://example.com'})"
+ }
+ ]
+ },
+ {
+ "id": "capture_exception",
+ "title": "capture_exception",
+ "description": "Capture an exception for error tracking.",
+ "details": "",
+ "category": "Error Tracking",
+ "params": [
+ {
+ "name": "exception",
+ "description": "The exception to capture.",
+ "isOptional": true,
+ "type": "BaseException"
+ },
+ {
+ "name": "kwargs",
+ "description": "",
+ "isOptional": true,
+ "type": "Unpack[OptionalCaptureArgs]"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[str]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "try:\n # Some code that might fail\n pass\nexcept Exception as e:\n posthog.capture_exception(e, 'user_distinct_id', properties=additional_properties)"
+ }
+ ]
+ },
+ {
+ "id": "evaluate_flags",
+ "title": "evaluate_flags",
+ "description": "Evaluate all feature flags for a user in a single call and return a :class:`FeatureFlagEvaluations` snapshot. Branch on ``.is_enabled()`` / ``.get_flag()`` and pass the same snapshot to :meth:`capture` via the ``flags`` option so events carry the exact flag values the code branched on. Prefer this over repeated ``get_feature_flag()`` calls and over ``capture(send_feature_flags=True)`` \u2014 it consolidates flag evaluation into a single ``/flags`` request per incoming request. Local evaluation is transparent: when the poller resolves a flag, the snapshot's ``$feature_flag_called`` events are tagged ``locally_evaluated=True`` and reason ``\"Evaluated locally\"``.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "distinct_id",
+ "description": "The user's distinct ID. If ``None``, falls back to the context distinct_id. If still unresolvable, returns an empty snapshot.",
+ "isOptional": false,
+ "type": "Number"
+ },
+ {
+ "name": "groups",
+ "description": "Mapping of group type to group key.",
+ "isOptional": true,
+ "type": "dict[str, str]"
+ },
+ {
+ "name": "person_properties",
+ "description": "Person properties to use for evaluation.",
+ "isOptional": true,
+ "type": "dict[str, Any]"
+ },
+ {
+ "name": "group_properties",
+ "description": "Group properties keyed by group type.",
+ "isOptional": true,
+ "type": "dict[str, dict[str, Any]]"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "If True, never fall back to remote evaluation \u2014 flags that can't be evaluated locally are simply omitted from the snapshot.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP lookup.",
+ "isOptional": true,
+ "type": "bool"
+ },
+ {
+ "name": "flag_keys",
+ "description": "Optional list of flag keys to scope the underlying ``/flags`` request to a subset.",
+ "isOptional": true,
+ "type": "list[str]"
+ },
+ {
+ "name": "device_id",
+ "description": "Optional device ID override. If not provided, falls back to the context device_id (which may be set via tracing headers). Used by experience-continuity flags to match users across distinct_id changes.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "FeatureFlagEvaluations"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "flags = posthog.evaluate_flags(\n \"user_123\",\n person_properties={\"plan\": \"enterprise\"},\n)\nif flags.is_enabled(\"new-dashboard\"):\n render_new_dashboard()\nposthog.capture(\"page_viewed\", distinct_id=\"user_123\", flags=flags)"
+ }
+ ]
+ },
+ {
+ "id": "feature_enabled",
+ "title": "feature_enabled",
+ "description": "Check if a feature flag is enabled for a user.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "key",
+ "description": "The feature flag key.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID of the user.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "A dictionary of group information.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "A dictionary of person properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "A dictionary of group properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to only evaluate locally.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "send_feature_flag_events",
+ "description": "Whether to send feature flag events.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this request.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "device_id",
+ "description": "The device ID for this request.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "is_my_flag_enabled = posthog.feature_enabled('flag-key', 'distinct_id_of_your_user')\nif is_my_flag_enabled:\n # Do something differently for this user\n # Optional: fetch the payload\n matched_flag_payload = posthog.get_feature_flag_payload('flag-key', 'distinct_id_of_your_user')"
+ }
+ ]
+ },
+ {
+ "id": "feature_flag_definitions",
+ "title": "feature_flag_definitions",
+ "description": "Return feature flag definitions loaded for local evaluation. Returns: The currently loaded feature flag definitions, or ``None`` before local evaluation has loaded definitions.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ }
+ },
+ {
+ "id": "flush",
+ "title": "flush",
+ "description": "Force a flush from the internal queue to the server. Do not use directly, call `shutdown()` instead.",
+ "details": "",
+ "category": null,
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "any"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "posthog.capture('event_name')\nposthog.flush() # Ensures the event is sent immediately"
+ }
+ ]
+ },
+ {
+ "id": "get_all_flags",
+ "title": "get_all_flags",
+ "description": "Get all feature flags for a user.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID of the user.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "A dictionary of group information.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "A dictionary of person properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "A dictionary of group properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to only evaluate locally.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this request.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "flag_keys_to_evaluate",
+ "description": "A list of specific flag keys to evaluate. If provided, only these flags will be evaluated, improving performance.",
+ "isOptional": true,
+ "type": "list[str]"
+ },
+ {
+ "name": "device_id",
+ "description": "The device ID for this request.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[dict[str, Union[bool, str]]]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "posthog.get_all_flags('distinct_id_of_your_user')"
+ }
+ ]
+ },
+ {
+ "id": "get_all_flags_and_payloads",
+ "title": "get_all_flags_and_payloads",
+ "description": "Get all feature flags and their payloads for a user.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID of the user.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "A dictionary of group information.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "A dictionary of person properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "A dictionary of group properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to only evaluate locally.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this request.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "flag_keys_to_evaluate",
+ "description": "A list of specific flag keys to evaluate. If provided, only these flags will be evaluated, improving performance.",
+ "isOptional": true,
+ "type": "list[str]"
+ },
+ {
+ "name": "device_id",
+ "description": "The device ID for this request.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "FlagsAndPayloads"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "posthog.get_all_flags_and_payloads('distinct_id_of_your_user')"
+ }
+ ]
+ },
+ {
+ "id": "get_feature_flag",
+ "title": "get_feature_flag",
+ "description": "Get multivariate feature flag value for a user.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "key",
+ "description": "The feature flag key.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID of the user.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "A dictionary of group information.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "A dictionary of person properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "A dictionary of group properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to only evaluate locally.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "send_feature_flag_events",
+ "description": "Whether to send feature flag events.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this request.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "device_id",
+ "description": "The device ID for this request.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Union[bool, str, any]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "enabled_variant = posthog.get_feature_flag('flag-key', 'distinct_id_of_your_user')\nif enabled_variant == 'variant-key': # replace 'variant-key' with the key of your variant\n # Do something differently for this user\n # Optional: fetch the payload\n matched_flag_payload = posthog.get_feature_flag_payload('flag-key', 'distinct_id_of_your_user')"
+ }
+ ]
+ },
+ {
+ "id": "get_feature_flag_payload",
+ "title": "get_feature_flag_payload",
+ "description": "Get the payload for a feature flag.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "key",
+ "description": "The feature flag key.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID of the user.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "match_value",
+ "description": "The specific flag value to get payload for.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "groups",
+ "description": "A dictionary of group information.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "A dictionary of person properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "A dictionary of group properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to only evaluate locally.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "send_feature_flag_events",
+ "description": "Deprecated. Use get_feature_flag() instead if you need events.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this request.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "device_id",
+ "description": "The device ID for this request.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "is_my_flag_enabled = posthog.feature_enabled('flag-key', 'distinct_id_of_your_user')\n\nif is_my_flag_enabled:\n # Do something differently for this user\n # Optional: fetch the payload\n matched_flag_payload = posthog.get_feature_flag_payload('flag-key', 'distinct_id_of_your_user')"
+ }
+ ]
+ },
+ {
+ "id": "get_feature_flag_result",
+ "title": "get_feature_flag_result",
+ "description": "Get a FeatureFlagResult object which contains the flag result and payload for a key by evaluating locally or remotely depending on whether local evaluation is enabled and the flag can be locally evaluated. This also captures the `$feature_flag_called` event unless `send_feature_flag_events` is `False`.",
+ "details": "",
+ "category": null,
+ "params": [
+ {
+ "name": "key",
+ "description": "The feature flag key.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID of the user.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "A dictionary of group information.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "A dictionary of person properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "A dictionary of group properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to only evaluate locally.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "send_feature_flag_events",
+ "description": "Whether to send feature flag events.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this request.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "device_id",
+ "description": "The device ID for this request.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[FeatureFlagResult]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "flag_result = posthog.get_feature_flag_result('flag-key', 'distinct_id_of_your_user')\nif flag_result and flag_result.get_value() == 'variant-key':\n # Do something differently for this user\n # Optional: fetch the payload\n matched_flag_payload = flag_result.payload"
+ }
+ ]
+ },
+ {
+ "id": "get_feature_flags_and_payloads",
+ "title": "get_feature_flags_and_payloads",
+ "description": "Get feature flags and payloads for a user.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID of the user.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "A dictionary of group information.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "A dictionary of person properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "A dictionary of group properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this request.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "flag_keys_to_evaluate",
+ "description": "A list of specific flag keys to evaluate. If provided, only these flags will be evaluated, improving performance.",
+ "isOptional": true,
+ "type": "list[str]"
+ },
+ {
+ "name": "device_id",
+ "description": "The device ID for this request.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "FlagsAndPayloads"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "result = posthog.get_feature_flags_and_payloads('')"
+ }
+ ]
+ },
+ {
+ "id": "get_feature_payloads",
+ "title": "get_feature_payloads",
+ "description": "Get feature flag payloads for a user.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID of the user.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "A dictionary of group information.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "A dictionary of person properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "A dictionary of group properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this request.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "flag_keys_to_evaluate",
+ "description": "A list of specific flag keys to evaluate. If provided, only these flags will be evaluated, improving performance.",
+ "isOptional": true,
+ "type": "list[str]"
+ },
+ {
+ "name": "device_id",
+ "description": "The device ID for this request.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "dict[str, str]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "payloads = posthog.get_feature_payloads('')"
+ }
+ ]
+ },
+ {
+ "id": "get_feature_variants",
+ "title": "get_feature_variants",
+ "description": "Get feature flag variants for a user.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID of the user.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "A dictionary of group information.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "A dictionary of person properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "A dictionary of group properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this request.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "flag_keys_to_evaluate",
+ "description": "A list of specific flag keys to evaluate. If provided, only these flags will be evaluated, improving performance.",
+ "isOptional": true,
+ "type": "list[str]"
+ },
+ {
+ "name": "device_id",
+ "description": "The device ID for this request.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "dict[str, Union[bool, str]]"
+ }
+ },
+ {
+ "id": "get_flags_decision",
+ "title": "get_flags_decision",
+ "description": "Get feature flags decision.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID of the user.",
+ "isOptional": false,
+ "type": "Number"
+ },
+ {
+ "name": "groups",
+ "description": "A dictionary of group information.",
+ "isOptional": true,
+ "type": "dict"
+ },
+ {
+ "name": "person_properties",
+ "description": "A dictionary of person properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "A dictionary of group properties.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this request.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "flag_keys_to_evaluate",
+ "description": "A list of specific flag keys to evaluate. If provided, only these flags will be evaluated, improving performance.",
+ "isOptional": true,
+ "type": "list[str]"
+ },
+ {
+ "name": "device_id",
+ "description": "The device ID for this request.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "FlagsResponse"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "decision = posthog.get_flags_decision('user123')"
+ }
+ ]
+ },
+ {
+ "id": "get_remote_config_payload",
+ "title": "get_remote_config_payload",
+ "description": "Get the payload for a remote config feature flag.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "key",
+ "description": "The remote config feature flag key.",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ }
+ },
+ {
+ "id": "group_identify",
+ "title": "group_identify",
+ "description": "Identify a group and set its properties.",
+ "details": "",
+ "category": "Identification",
+ "params": [
+ {
+ "name": "group_type",
+ "description": "The type of group (e.g., 'company', 'team').",
+ "isOptional": true,
+ "type": "str"
+ },
+ {
+ "name": "group_key",
+ "description": "The unique identifier for the group.",
+ "isOptional": true,
+ "type": "str"
+ },
+ {
+ "name": "properties",
+ "description": "A dictionary of properties to set on the group.",
+ "isOptional": true,
+ "type": "dict[str, Any]"
+ },
+ {
+ "name": "timestamp",
+ "description": "The timestamp of the event.",
+ "isOptional": false,
+ "type": "datetime"
+ },
+ {
+ "name": "uuid",
+ "description": "A unique identifier for the event.",
+ "isOptional": true,
+ "type": "str"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP for this event.",
+ "isOptional": true,
+ "type": "bool"
+ },
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID of the user performing the action.",
+ "isOptional": false,
+ "type": "Number"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[str]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "posthog.group_identify('company', 'company_id_in_your_db', {\n 'name': 'Awesome Inc.',\n 'employees': 11\n})"
+ }
+ ]
+ },
+ {
+ "id": "join",
+ "title": "join",
+ "description": "End the consumer thread once the queue is empty. Do not use directly, call `shutdown()` instead.",
+ "details": "",
+ "category": null,
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "any"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "posthog.join()"
+ }
+ ]
+ },
+ {
+ "id": "load_feature_flags",
+ "title": "load_feature_flags",
+ "description": "Load feature flags for local evaluation.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "posthog.load_feature_flags()"
+ }
+ ]
+ },
+ {
+ "id": "new_context",
+ "title": "new_context",
+ "description": "Create a new context for managing shared state. Learn more about [contexts](/docs/libraries/python#contexts).",
+ "details": "",
+ "category": "Contexts",
+ "params": [
+ {
+ "name": "fresh",
+ "description": "Whether to create a fresh context that doesn't inherit from parent.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "capture_exceptions",
+ "description": "Whether to automatically capture exceptions in this context.",
+ "isOptional": false,
+ "type": "bool"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "with posthog.new_context():\n identify_context('')\n posthog.capture('event_name')"
+ }
+ ]
+ },
+ {
+ "id": "set",
+ "title": "set",
+ "description": "Set properties on a person profile.",
+ "details": "",
+ "category": "Identification",
+ "params": [
+ {
+ "name": "kwargs",
+ "description": "",
+ "isOptional": true,
+ "type": "Unpack[OptionalSetArgs]"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[str]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Set with distinct id",
+ "code": "# Set with distinct id\nposthog.set(distinct_id='user123', properties={'name': 'Max Hedgehog'})"
+ }
+ ]
+ },
+ {
+ "id": "set_once",
+ "title": "set_once",
+ "description": "Set properties on a person profile only if they haven't been set before.",
+ "details": "",
+ "category": "Identification",
+ "params": [
+ {
+ "name": "kwargs",
+ "description": "",
+ "isOptional": true,
+ "type": "Unpack[OptionalSetArgs]"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[str]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "posthog.set_once(distinct_id='user123', properties={'initial_signup_date': '2024-01-01'})"
+ }
+ ]
+ },
+ {
+ "id": "shutdown",
+ "title": "shutdown",
+ "description": "Flush all messages and cleanly shutdown the client. Call this before the process ends in serverless environments to avoid data loss.",
+ "details": "",
+ "category": null,
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "any"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "posthog.shutdown()"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "id": "PostHogModule",
+ "title": "PostHog Module Functions",
+ "description": "Global functions available in the PostHog module",
+ "functions": [
+ {
+ "id": "alias",
+ "title": "alias",
+ "description": "Associate user behaviour before and after they e.g. register, login, or perform some other identifying action.",
+ "details": "To marry up whatever a user does before they sign up or log in with what they do after you need to make an alias call. This will allow you to answer questions like \"Which marketing channels leads to users churning after a month?\" or \"What do users do on our website before signing up?\". Particularly useful for associating user behaviour before and after they e.g. register, login, or perform some other identifying action.",
+ "category": "Identification",
+ "params": [
+ {
+ "name": "previous_id",
+ "description": "The unique ID of the user before",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "distinct_id",
+ "description": "The current unique id",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "timestamp",
+ "description": "Optional timestamp for the event",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "uuid",
+ "description": "Optional UUID for the event",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP lookup",
+ "isOptional": false,
+ "type": "any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Alias user",
+ "code": "# Alias user\nfrom posthog import alias\nalias(previous_id='distinct_id', distinct_id='alias_id')"
+ }
+ ]
+ },
+ {
+ "id": "capture",
+ "title": "capture",
+ "description": "Capture anything a user does within your system.",
+ "details": "Capture allows you to capture anything a user does within your system, which you can later use in PostHog to find patterns in usage, work out which features to improve or where people are giving up. A capture call requires an event name to specify the event. We recommend using [verb] [noun], like `movie played` or `movie updated` to easily identify what your events mean later on. Capture takes a number of optional arguments, which are defined by the `OptionalCaptureArgs` type.",
+ "category": "Events",
+ "params": [
+ {
+ "name": "event",
+ "description": "The event name to specify the event **kwargs: Optional arguments including:",
+ "isOptional": true,
+ "type": "str"
+ },
+ {
+ "name": "kwargs",
+ "description": "",
+ "isOptional": true,
+ "type": "Unpack[OptionalCaptureArgs]"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[str]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Context and capture usage",
+ "code": "# Context and capture usage\nfrom posthog import new_context, identify_context, tag_context, capture\n# Enter a new context (e.g. a request/response cycle, an instance of a background job, etc)\nwith new_context():\n # Associate this context with some user, by distinct_id\n identify_context('some user')\n\n # Capture an event, associated with the context-level distinct ID ('some user')\n capture('movie started')\n\n # Capture an event associated with some other user (overriding the context-level distinct ID)\n capture('movie joined', distinct_id='some-other-user')\n\n # Capture an event with some properties\n capture('movie played', properties={'movie_id': '123', 'category': 'romcom'})\n\n # Capture an event with some properties\n capture('purchase', properties={'product_id': '123', 'category': 'romcom'})\n # Capture an event with some associated group\n capture('purchase', groups={'company': 'id:5'})\n\n # Adding a tag to the current context will cause it to appear on all subsequent events\n tag_context('some-tag', 'some-value')\n\n capture('another-event') # Will be captured with `'some-tag': 'some-value'` in the properties dict"
+ },
+ {
+ "id": "example_2",
+ "name": "Set event properties",
+ "code": "# Set event properties\nfrom posthog import capture\ncapture(\n \"user_signed_up\",\n distinct_id=\"distinct_id_of_the_user\",\n properties={\n \"login_type\": \"email\",\n \"is_free_trial\": \"true\"\n }\n)"
+ }
+ ]
+ },
+ {
+ "id": "capture_exception",
+ "title": "capture_exception",
+ "description": "Capture exceptions that happen in your code.",
+ "details": "Capture exception is idempotent - if it is called twice with the same exception instance, only a occurrence will be tracked in posthog. This is because, generally, contexts will cause exceptions to be captured automatically. However, to ensure you track an exception, if you catch and do not re-raise it, capturing it manually is recommended, unless you are certain it will have crossed a context boundary (e.g. by existing a `with posthog.new_context():` block already). If the passed exception was raised and caught, the captured stack trace will consist of every frame between where the exception was raised and the point at which it is captured (the \"traceback\"). If the passed exception was never raised, e.g. if you call `posthog.capture_exception(ValueError(\"Some Error\"))`, the stack trace captured will be the full stack trace at the moment the exception was captured. Note that heavy use of contexts will lead to truncated stack traces, as the exception will be captured by the context entered most recently, which may not be the point you catch the exception for the final time in your code. It's recommended to use contexts sparingly, for this reason. `capture_exception` takes the same set of optional arguments as `capture`.",
+ "category": "Events",
+ "params": [
+ {
+ "name": "exception",
+ "description": "The exception to capture. If not provided, the current exception is captured via `sys.exc_info()` **kwargs: Optional capture arguments including distinct_id, properties, timestamp, uuid, groups, flags, send_feature_flags, and disable_geoip.",
+ "isOptional": false,
+ "type": "BaseException"
+ },
+ {
+ "name": "kwargs",
+ "description": "",
+ "isOptional": true,
+ "type": "Unpack[OptionalCaptureArgs]"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[str]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Capture exception",
+ "code": "# Capture exception\nfrom posthog import capture_exception\ntry:\n risky_operation()\nexcept Exception as e:\n capture_exception(e)"
+ }
+ ]
+ },
+ {
+ "id": "evaluate_flags",
+ "title": "evaluate_flags",
+ "description": "Evaluate all feature flags for a user in a single call and return a :class:`FeatureFlagEvaluations` snapshot. Branch on ``.is_enabled()`` / ``.get_flag()`` and pass the same snapshot to ``capture()`` via the ``flags`` option so events carry the exact flag values the code branched on. Prefer this over repeated ``get_feature_flag()`` calls and over ``capture(send_feature_flags=True)`` \u2014 it consolidates flag evaluation into a single ``/flags`` request per incoming request.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "distinct_id",
+ "description": "The user's distinct ID. If ``None``, falls back to the context distinct_id. If still unresolvable, returns an empty snapshot.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "Mapping of group type to group key.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "Person properties to use for evaluation.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "Group properties keyed by group type.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "If ``True``, never fall back to remote evaluation.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP lookup.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "flag_keys",
+ "description": "Optional list of flag keys. When provided, only these flags are evaluated \u2014 the underlying ``/flags`` request asks the server for just this subset, which makes the response smaller and the request cheaper. Use this when you only need a handful of flags out of many.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "device_id",
+ "description": "Optional device ID override. If not provided, falls back to the context device_id (which may be set via tracing headers). Used by experience-continuity flags to match users across distinct_id changes.",
+ "isOptional": false,
+ "type": "any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "FeatureFlagEvaluations"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import evaluate_flags, capture\nflags = evaluate_flags(\"user_123\", person_properties={\"plan\": \"enterprise\"})\nif flags.is_enabled(\"new-dashboard\"):\n render_new_dashboard()\ncapture(\"page_viewed\", distinct_id=\"user_123\", flags=flags)"
+ }
+ ]
+ },
+ {
+ "id": "feature_enabled",
+ "title": "feature_enabled",
+ "description": "Use feature flags to enable or disable features for users.",
+ "details": "You can call `posthog.load_feature_flags()` before to make sure you're not doing unexpected requests.",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "key",
+ "description": "The feature flag key",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "distinct_id",
+ "description": "The user's distinct ID",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "Groups mapping",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "Person properties",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "Group properties",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to evaluate only locally",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "send_feature_flag_events",
+ "description": "Whether to send feature flag events",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP lookup",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "device_id",
+ "description": "Optional device ID override for experience-continuity flags",
+ "isOptional": false,
+ "type": "any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Boolean feature flag",
+ "code": "# Boolean feature flag\nfrom posthog import feature_enabled, get_feature_flag_payload\nis_my_flag_enabled = feature_enabled('flag-key', 'distinct_id_of_your_user')\nif is_my_flag_enabled:\n matched_flag_payload = get_feature_flag_payload('flag-key', 'distinct_id_of_your_user')"
+ }
+ ]
+ },
+ {
+ "id": "feature_flag_definitions",
+ "title": "feature_flag_definitions",
+ "description": "Returns loaded feature flags.",
+ "details": "Returns loaded feature flags, if any. Helpful for debugging what flag information you have loaded.",
+ "category": "Feature flags",
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import feature_flag_definitions\ndefinitions = feature_flag_definitions()"
+ }
+ ]
+ },
+ {
+ "id": "flush",
+ "title": "flush",
+ "description": "Tell the client to flush all queued events.",
+ "details": "",
+ "category": "Client management",
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "any"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import flush\nflush()"
+ }
+ ]
+ },
+ {
+ "id": "get_all_flags",
+ "title": "get_all_flags",
+ "description": "Get all flags for a given user.",
+ "details": "Flags are key-value pairs where the key is the flag key and the value is the flag variant, or True, or False.",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "distinct_id",
+ "description": "The user's distinct ID",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "Groups mapping",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "Person properties",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "Group properties",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to evaluate only locally",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP lookup",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "device_id",
+ "description": "Optional device ID override for experience-continuity flags",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "flag_keys_to_evaluate",
+ "description": "Optional list of flag keys to evaluate (evaluates all if None)",
+ "isOptional": false,
+ "type": "any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[dict[str, FeatureFlag]]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "All flags for user",
+ "code": "# All flags for user\nfrom posthog import get_all_flags\nget_all_flags('distinct_id_of_your_user')"
+ }
+ ]
+ },
+ {
+ "id": "get_all_flags_and_payloads",
+ "title": "get_all_flags_and_payloads",
+ "description": "Get all feature flag values and payloads for a user.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "distinct_id",
+ "description": "The user's distinct ID.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "Mapping of group type to group key.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "Person properties to use for evaluation.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "Group properties keyed by group type.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to evaluate only locally.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP lookup.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "device_id",
+ "description": "Optional device ID override for experience-continuity flags.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "flag_keys_to_evaluate",
+ "description": "Optional list of flag keys to evaluate. Evaluates all flags when omitted.",
+ "isOptional": false,
+ "type": "any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "FlagsAndPayloads"
+ }
+ },
+ {
+ "id": "get_feature_flag",
+ "title": "get_feature_flag",
+ "description": "Get feature flag variant for users. Used with experiments.",
+ "details": "`groups` are a mapping from group type to group key. So, if you have a group type of \"organization\" and a group key of \"5\", you would pass groups={\"organization\": \"5\"}. `group_properties` take the format: { group_type_name: { group_properties } }. So, for example, if you have the group type \"organization\" and the group key \"5\", with the properties name, and employee count, you'll send these as: group_properties={\"organization\": {\"name\": \"PostHog\", \"employees\": 11}}.",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "key",
+ "description": "The feature flag key",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "distinct_id",
+ "description": "The user's distinct ID",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "Groups mapping from group type to group key",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "Person properties",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "Group properties in format { group_type_name: { group_properties } }",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to evaluate only locally",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "send_feature_flag_events",
+ "description": "Whether to send feature flag events",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP lookup",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "device_id",
+ "description": "Optional device ID override for experience-continuity flags",
+ "isOptional": false,
+ "type": "any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[FeatureFlag]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Multivariate feature flag",
+ "code": "# Multivariate feature flag\nfrom posthog import get_feature_flag, get_feature_flag_payload\nenabled_variant = get_feature_flag('flag-key', 'distinct_id_of_your_user')\nif enabled_variant == 'variant-key':\n matched_flag_payload = get_feature_flag_payload('flag-key', 'distinct_id_of_your_user')"
+ }
+ ]
+ },
+ {
+ "id": "get_feature_flag_payload",
+ "title": "get_feature_flag_payload",
+ "description": "Get the payload associated with a feature flag value. Deprecated for new code. Prefer ``evaluate_flags()`` and ``flags.get_flag_payload(key)`` so flag evaluation happens once per request.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [
+ {
+ "name": "key",
+ "description": "The feature flag key.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "distinct_id",
+ "description": "The user's distinct ID.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "match_value",
+ "description": "Optional flag value to use when selecting a payload.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "Mapping of group type to group key.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "Person properties to use for evaluation.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "Group properties keyed by group type.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to evaluate only locally.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "send_feature_flag_events",
+ "description": "Whether to send a $feature_flag_called event.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP lookup.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "device_id",
+ "description": "Optional device ID override for experience-continuity flags.",
+ "isOptional": false,
+ "type": "any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[str]"
+ }
+ },
+ {
+ "id": "get_feature_flag_result",
+ "title": "get_feature_flag_result",
+ "description": "Get a FeatureFlagResult object which contains the flag result and payload. This method evaluates a feature flag and returns a FeatureFlagResult object containing: - enabled: Whether the flag is enabled - variant: The variant value if the flag has variants - payload: The payload associated with the flag (automatically deserialized from JSON) - key: The flag key - reason: Why the flag was enabled/disabled",
+ "details": "",
+ "category": null,
+ "params": [
+ {
+ "name": "key",
+ "description": "The feature flag key.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "distinct_id",
+ "description": "The user's distinct ID.",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "groups",
+ "description": "Mapping of group type to group key.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "person_properties",
+ "description": "Person properties to use for evaluation.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "group_properties",
+ "description": "Group properties keyed by group type.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "only_evaluate_locally",
+ "description": "Whether to evaluate only locally.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "send_feature_flag_events",
+ "description": "Whether to send a $feature_flag_called event.",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP lookup.",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "device_id",
+ "description": "Optional device ID override for experience-continuity flags.",
+ "isOptional": false,
+ "type": "any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ }
+ },
+ {
+ "id": "get_remote_config_payload",
+ "title": "get_remote_config_payload",
+ "description": "Get the payload for a remote config feature flag.",
+ "details": "",
+ "category": null,
+ "params": [
+ {
+ "name": "key",
+ "description": "The key of the feature flag",
+ "isOptional": true,
+ "type": "any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ }
+ },
+ {
+ "id": "get_tags",
+ "title": "get_tags",
+ "description": "Get all tags from the current context. Returns: Dict of all tags in the current context",
+ "details": "",
+ "category": "Contexts",
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "dict[str, Any]"
+ }
+ },
+ {
+ "id": "group_identify",
+ "title": "group_identify",
+ "description": "Set properties on a group.",
+ "details": "",
+ "category": "Identification",
+ "params": [
+ {
+ "name": "group_type",
+ "description": "Type of your group",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "group_key",
+ "description": "Unique identifier of the group",
+ "isOptional": true,
+ "type": "any"
+ },
+ {
+ "name": "properties",
+ "description": "Properties to set on the group",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "timestamp",
+ "description": "Optional timestamp for the event",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "uuid",
+ "description": "Optional UUID for the event",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "disable_geoip",
+ "description": "Whether to disable GeoIP lookup",
+ "isOptional": false,
+ "type": "any"
+ },
+ {
+ "name": "distinct_id",
+ "description": "Optional distinct ID of the user performing the action",
+ "isOptional": false,
+ "type": "any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Group identify",
+ "code": "# Group identify\nfrom posthog import group_identify\ngroup_identify('company', 'company_id_in_your_db', {\n 'name': 'Awesome Inc.',\n 'employees': 11\n})"
+ }
+ ]
+ },
+ {
+ "id": "identify_context",
+ "title": "identify_context",
+ "description": "Identify the current context with a distinct ID.",
+ "details": "",
+ "category": "Identification",
+ "params": [
+ {
+ "name": "distinct_id",
+ "description": "The distinct ID to associate with the current context and its children",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import identify_context\nidentify_context(\"user_123\")"
+ }
+ ]
+ },
+ {
+ "id": "join",
+ "title": "join",
+ "description": "Block program until the client clears the queue. Used during program shutdown. You should use `shutdown()` directly in most cases.",
+ "details": "",
+ "category": "Client management",
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "any"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import join\njoin()"
+ }
+ ]
+ },
+ {
+ "id": "load_feature_flags",
+ "title": "load_feature_flags",
+ "description": "Load feature flag definitions from PostHog.",
+ "details": "",
+ "category": "Feature flags",
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import load_feature_flags\nload_feature_flags()"
+ }
+ ]
+ },
+ {
+ "id": "new_context",
+ "title": "new_context",
+ "description": "Create a new context scope that will be active for the duration of the with block.",
+ "details": "",
+ "category": "Contexts",
+ "params": [
+ {
+ "name": "fresh",
+ "description": "Whether to start with a fresh context (default: False)",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "capture_exceptions",
+ "description": "Whether to capture exceptions raised within the context (default: True)",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "client",
+ "description": "Optional Posthog client instance to use for this context (default: None)",
+ "isOptional": true,
+ "type": "Client"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import new_context, tag, capture\nwith new_context():\n tag(\"request_id\", \"123\")\n capture(\"event_name\", properties={\"property\": \"value\"})"
+ }
+ ]
+ },
+ {
+ "id": "scoped",
+ "title": "scoped",
+ "description": "Decorator that creates a new context for the function.",
+ "details": "",
+ "category": "Contexts",
+ "params": [
+ {
+ "name": "fresh",
+ "description": "Whether to start with a fresh context (default: False)",
+ "isOptional": false,
+ "type": "bool"
+ },
+ {
+ "name": "capture_exceptions",
+ "description": "Whether to capture and track exceptions with posthog error tracking (default: True)",
+ "isOptional": false,
+ "type": "bool"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import scoped, tag, capture\n@scoped()\ndef process_payment(payment_id):\n tag(\"payment_id\", payment_id)\n capture(\"payment_started\")"
+ }
+ ]
+ },
+ {
+ "id": "set",
+ "title": "set",
+ "description": "Set properties on a user record.",
+ "details": "This will overwrite previous people property values. Generally operates similar to `capture`, with distinct_id being an optional argument, defaulting to the current context's distinct ID. If there is no context-level distinct ID, and no override distinct_id is passed, this function will do nothing. Context tags are folded into $set properties, so tagging the current context and then calling `set` will cause those tags to be set on the user (unlike capture, which causes them to just be set on the event).",
+ "category": "Identification",
+ "params": [
+ {
+ "name": "kwargs",
+ "description": "",
+ "isOptional": true,
+ "type": "Unpack[OptionalSetArgs]"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[str]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Set person properties",
+ "code": "# Set person properties\nfrom posthog import set\nset(distinct_id='distinct_id', properties={'name': 'Max Hedgehog'})"
+ }
+ ]
+ },
+ {
+ "id": "set_capture_exception_code_variables_context",
+ "title": "set_capture_exception_code_variables_context",
+ "description": "Override code-variable capture for exceptions in the current context.",
+ "details": "",
+ "category": "Contexts",
+ "params": [
+ {
+ "name": "enabled",
+ "description": "Whether exceptions captured in this context should include local variable values from stack frames.",
+ "isOptional": true,
+ "type": "bool"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ }
+ },
+ {
+ "id": "set_code_variables_ignore_patterns_context",
+ "title": "set_code_variables_ignore_patterns_context",
+ "description": "Override code-variable ignore patterns for exceptions in the current context.",
+ "details": "",
+ "category": "Contexts",
+ "params": [
+ {
+ "name": "ignore_patterns",
+ "description": "Variable-name patterns that should be omitted entirely when code variables are captured.",
+ "isOptional": true,
+ "type": "list"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ }
+ },
+ {
+ "id": "set_code_variables_mask_patterns_context",
+ "title": "set_code_variables_mask_patterns_context",
+ "description": "Override code-variable mask patterns for exceptions in the current context.",
+ "details": "",
+ "category": "Contexts",
+ "params": [
+ {
+ "name": "mask_patterns",
+ "description": "Variable-name patterns whose values should be replaced with ``***`` when code variables are captured.",
+ "isOptional": true,
+ "type": "list"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ }
+ },
+ {
+ "id": "set_context_device_id",
+ "title": "set_context_device_id",
+ "description": "Set the device ID for the current context, associating all feature flag requests in this or child contexts with the given device ID.",
+ "details": "",
+ "category": "Contexts",
+ "params": [
+ {
+ "name": "device_id",
+ "description": "The device ID to associate with the current context and its children",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import set_context_device_id\nset_context_device_id(\"device_123\")"
+ }
+ ]
+ },
+ {
+ "id": "set_context_session",
+ "title": "set_context_session",
+ "description": "Set the session ID for the current context.",
+ "details": "",
+ "category": "Contexts",
+ "params": [
+ {
+ "name": "session_id",
+ "description": "The session ID to associate with the current context and its children",
+ "isOptional": true,
+ "type": "str"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import set_context_session\nset_context_session(\"session_123\")"
+ }
+ ]
+ },
+ {
+ "id": "set_once",
+ "title": "set_once",
+ "description": "Set properties on a user record, only if they do not yet exist.",
+ "details": "This will not overwrite previous people property values, unlike `set`. Otherwise, operates in an identical manner to `set`.",
+ "category": "Identification",
+ "params": [
+ {
+ "name": "kwargs",
+ "description": "",
+ "isOptional": true,
+ "type": "Unpack[OptionalSetArgs]"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Optional[str]"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Set property once",
+ "code": "# Set property once\nfrom posthog import set_once\nset_once(distinct_id='distinct_id', properties={'initial_url': '/blog'})"
+ }
+ ]
+ },
+ {
+ "id": "setup",
+ "title": "setup",
+ "description": "Create or return the global PostHog client configured by module settings. Most applications should either instantiate ``Posthog`` directly or set ``posthog.api_key``/other module settings before calling top-level helpers. ``setup()`` is called automatically by global APIs such as ``capture()``. Returns: The global ``Client`` instance. If ``api_key`` is missing or blank, the client is disabled and module-level calls become no-ops.",
+ "details": "",
+ "category": "Initialization",
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "Client"
+ }
+ },
+ {
+ "id": "shutdown",
+ "title": "shutdown",
+ "description": "Flush all messages and cleanly shutdown the client.",
+ "details": "",
+ "category": "Client management",
+ "params": [],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "any"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import shutdown\nshutdown()"
+ }
+ ]
+ },
+ {
+ "id": "tag",
+ "title": "tag",
+ "description": "Add a tag to the current context.",
+ "details": "",
+ "category": "Contexts",
+ "params": [
+ {
+ "name": "name",
+ "description": "The tag key",
+ "isOptional": true,
+ "type": "str"
+ },
+ {
+ "name": "value",
+ "description": "The tag value",
+ "isOptional": true,
+ "type": "Any"
+ }
+ ],
+ "showDocs": true,
+ "releaseTag": "public",
+ "returnType": {
+ "id": "return_type",
+ "name": "None"
+ },
+ "examples": [
+ {
+ "id": "example_1",
+ "name": "Example 1",
+ "code": "from posthog import tag\ntag(\"user_id\", \"123\")"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "categories": [
+ "Initialization",
+ "Identification",
+ "Capture",
+ "Error Tracking",
+ "Feature flags",
+ "Contexts",
+ "Events",
+ "Client management"
+ ]
+}
\ No newline at end of file
diff --git a/references/posthog-python-references-latest.json b/references/posthog-python-references-latest.json
index efe2ea4b..153a5a63 100644
--- a/references/posthog-python-references-latest.json
+++ b/references/posthog-python-references-latest.json
@@ -2,7 +2,7 @@
"id": "posthog-python",
"hogRef": "0.3",
"info": {
- "version": "7.19.0",
+ "version": "7.19.1",
"id": "posthog-python",
"title": "PostHog Python SDK",
"description": "Integrate PostHog into any python application.",
@@ -720,7 +720,7 @@
"releaseTag": "public",
"returnType": {
"id": "return_type",
- "name": "None"
+ "name": "Optional[str]"
},
"examples": [
{
@@ -901,7 +901,7 @@
"releaseTag": "public",
"returnType": {
"id": "return_type",
- "name": "None"
+ "name": "any"
},
"examples": [
{
@@ -1630,7 +1630,7 @@
"releaseTag": "public",
"returnType": {
"id": "return_type",
- "name": "None"
+ "name": "any"
},
"examples": [
{
@@ -1762,7 +1762,7 @@
"releaseTag": "public",
"returnType": {
"id": "return_type",
- "name": "None"
+ "name": "any"
},
"examples": [
{
@@ -1894,7 +1894,7 @@
"releaseTag": "public",
"returnType": {
"id": "return_type",
- "name": "None"
+ "name": "Optional[str]"
},
"examples": [
{
@@ -2082,7 +2082,7 @@
"releaseTag": "public",
"returnType": {
"id": "return_type",
- "name": "None"
+ "name": "any"
},
"examples": [
{
@@ -2583,7 +2583,7 @@
"releaseTag": "public",
"returnType": {
"id": "return_type",
- "name": "None"
+ "name": "any"
},
"examples": [
{
@@ -2888,7 +2888,7 @@
"releaseTag": "public",
"returnType": {
"id": "return_type",
- "name": "None"
+ "name": "any"
},
"examples": [
{
From 3366456b078fb43e966684c6c209e4b099fd8ee7 Mon Sep 17 00:00:00 2001
From: Manoel Aranda Neto <5731772+marandaneto@users.noreply.github.com>
Date: Mon, 15 Jun 2026 15:25:32 +0200
Subject: [PATCH 7/8] docs: centralize SDK examples in official docs (#661)
* docs: centralize SDK examples in official docs
* address pr review feedback
* restore fastapi playground usage docs
* restore python support table in readme
* update readme framework doc links
---
BEFORE_SEND.md | 237 -------------------------------------------------
README.md | 16 +++-
2 files changed, 14 insertions(+), 239 deletions(-)
delete mode 100644 BEFORE_SEND.md
diff --git a/BEFORE_SEND.md b/BEFORE_SEND.md
deleted file mode 100644
index a3696d3d..00000000
--- a/BEFORE_SEND.md
+++ /dev/null
@@ -1,237 +0,0 @@
-# Before Send Hook
-
-The `before_send` parameter allows you to modify or filter events before they are sent to PostHog. This is useful for:
-
-- **Privacy**: Removing or masking sensitive data (PII)
-- **Filtering**: Dropping unwanted events (test events, internal users, etc.)
-- **Enhancement**: Adding custom properties to all events
-- **Transformation**: Modifying event names or property formats
-
-## Basic Usage
-
-```python
-import posthog
-from typing import Optional, Dict, Any
-
-def my_before_send(event: Dict[str, Any]) -> Optional[Dict[str, Any]]:
- """
- Process event before sending to PostHog.
-
- Args:
- event: The event dictionary containing 'event', 'distinct_id', 'properties', etc.
-
- Returns:
- Modified event dictionary to send, or None to drop the event
- """
- # Your processing logic here
- return event
-
-# Initialize client with before_send hook
-client = posthog.Client(
- api_key="your-project-api-key",
- before_send=my_before_send
-)
-```
-
-## Common Use Cases
-
-### 1. Filter Out Events
-
-```python
-from typing import Optional, Any
-
-def filter_events_by_property_or_event_name(event: dict[str, Any]) -> Optional[dict[str, Any]]:
- """Drop events from internal users or test environments."""
- properties = event.get("properties", {})
-
- # Choose some property from your events
- event_source = properties.get("event_source", "")
- if event_source.endswith("internal"):
- return None # Drop the event
-
- # Filter out test events
- if event.get("event") == "test_event":
- return None
-
- return event
-```
-
-### 2. Remove/Mask PII Data
-
-```python
-from typing import Optional, Any
-
-def scrub_pii(event: dict[str, Any]) -> Optional[dict[str, Any]]:
- """Remove or mask personally identifiable information."""
- properties = event.get("properties", {})
-
- # Mask email but keep domain for analytics
- if "email" in properties:
- email = properties["email"]
- if "@" in email:
- domain = email.split("@")[1]
- properties["email"] = f"***@{domain}"
- else:
- properties["email"] = "***"
-
- # Remove sensitive fields entirely
- sensitive_fields = ["my_business_info", "secret_things"]
- for field in sensitive_fields:
- properties.pop(field, None)
-
- return event
-```
-
-### 3. Add Custom Properties
-
-```python
-from typing import Optional, Any
-
-from datetime import datetime
-from typing import Optional, Any
-
-def add_context(event: dict[str, Any]) -> Optional[dict[str, Any]]:
- """Add custom properties to all events."""
- if "properties" not in event:
- event["properties"] = {}
-
- event["properties"].update({
- "app_version": "2.1.0",
- "environment": "production",
- "processed_at": datetime.now().isoformat()
- })
-
- return event
-```
-
-### 4. Transform Event Names
-
-```python
-from typing import Optional, Any
-
-def normalize_event_names(event: dict[str, Any]) -> Optional[dict[str, Any]]:
- """Convert event names to a consistent format."""
- original_event = event.get("event")
- if original_event:
- # Convert to snake_case
- normalized = original_event.lower().replace(" ", "_").replace("-", "_")
- event["event"] = f"app_{normalized}"
-
- return event
-```
-
-### 5. Log and drop in "dev" mode
-
-When running in local dev often, you want to log but drop all events
-
-
-```python
-from typing import Optional, Any
-
-def log_and_drop_all(event: dict[str, Any]) -> Optional[dict[str, Any]]:
- """Convert event names to a consistent format."""
- print(event)
-
- return None
-```
-
-### 6. Combined Processing
-
-```python
-from typing import Optional, Any
-
-def comprehensive_processor(event: dict[str, Any]) -> Optional[dict[str, Any]]:
- """Apply multiple transformations in sequence."""
-
- # Step 1: Filter unwanted events
- if should_drop_event(event):
- return None
-
- # Step 2: Scrub PII
- event = scrub_pii(event)
-
- # Step 3: Add context
- event = add_context(event)
-
- # Step 4: Normalize names
- event = normalize_event_names(event)
-
- return event
-
-def should_drop_event(event: dict[str, Any]) -> bool:
- """Determine if event should be dropped."""
- # Your filtering logic
- return False
-```
-
-## Error Handling
-
-If your `before_send` function raises an exception, PostHog will:
-
-1. Log the error
-2. Continue with the original, unmodified event
-3. Not crash your application
-
-```python
-from typing import Optional, Any
-
-def risky_before_send(event: dict[str, Any]) -> Optional[dict[str, Any]]:
- # If this raises an exception, the original event will be sent
- risky_operation()
- return event
-```
-
-## Complete Example
-
-```python
-import posthog
-from typing import Optional, Any
-import re
-
-def production_before_send(event: dict[str, Any]) -> Optional[dict[str, Any]]:
- try:
- properties = event.get("properties", {})
-
- # 1. Filter out bot traffic
- user_agent = properties.get("$user_agent", "")
- if re.search(r'bot|crawler|spider', user_agent, re.I):
- return None
-
- # 2. Filter out internal traffic
- ip = properties.get("$ip", "")
- if ip.startswith("192.168.") or ip.startswith("10."):
- return None
-
- # 3. Scrub email PII but keep domain
- if "email" in properties:
- email = properties["email"]
- if "@" in email:
- domain = email.split("@")[1]
- properties["email"] = f"***@{domain}"
-
- # 4. Add custom context
- properties.update({
- "app_version": "1.0.0",
- "build_number": "123"
- })
-
- # 5. Normalize event name
- if event.get("event"):
- event["event"] = event["event"].lower().replace(" ", "_")
-
- return event
-
- except Exception as e:
- # Log error but don't crash
- print(f"Error in before_send: {e}")
- return event # Return original event on error
-
-# Usage
-client = posthog.Client(
- api_key="your-api-key",
- before_send=production_before_send
-)
-
-# All events will now be processed by your before_send function
-client.capture("user_123", "Page View", {"url": "/home"})
-```
\ No newline at end of file
diff --git a/README.md b/README.md
index db066c8a..e8e2f708 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,9 @@
-Please see the [Python integration docs](https://posthog.com/docs/integrations/python-integration) for details.
+Please see the main [PostHog docs](https://posthog.com/docs).
+
+SDK usage examples and code snippets live in the official documentation so they stay up to date.
## Python Version Support
@@ -20,6 +22,16 @@ Please see the [Python integration docs](https://posthog.com/docs/integrations/p
| 7.0.0 - 7.0.1 | 3.10, 3.11, 3.12, 3.13 | Dropped Python 3.9 support |
| 4.0.1 - 6.x | 3.9, 3.10, 3.11, 3.12, 3.13 | Python 3.9+ required |
+## Documentation
+
+- [Python library docs](https://posthog.com/docs/libraries/python)
+- [Django framework docs](https://posthog.com/docs/libraries/django)
+- [Flask framework docs](https://posthog.com/docs/libraries/flask)
+
## Contributing
-See [CONTRIBUTING.md](CONTRIBUTING.md) for local setup, test, and development workflow instructions.
+See [CONTRIBUTING.md](CONTRIBUTING.md) for local setup and test instructions.
+
+## Releasing
+
+See [RELEASING.md](RELEASING.md).
From 0762cded34a5485e8cdec89ecb1d1c2d14fa140a Mon Sep 17 00:00:00 2001
From: Manoel Aranda Neto
Date: Mon, 15 Jun 2026 17:38:22 +0200
Subject: [PATCH 8/8] ci: add public API snapshot check
---
.github/scripts/check_public_api.py | 275 ++++++
.github/workflows/ci.yml | 14 +
.github/workflows/release.yml | 1 -
Makefile | 8 +-
pyproject.toml | 1 +
references/public_api_snapshot.txt | 1193 +++++++++++++++++++++++++++
uv.lock | 39 +-
7 files changed, 1528 insertions(+), 3 deletions(-)
create mode 100644 .github/scripts/check_public_api.py
create mode 100644 references/public_api_snapshot.txt
diff --git a/.github/scripts/check_public_api.py b/.github/scripts/check_public_api.py
new file mode 100644
index 00000000..4c258ed9
--- /dev/null
+++ b/.github/scripts/check_public_api.py
@@ -0,0 +1,275 @@
+#!/usr/bin/env python3
+"""Generate and check the Python SDK public API snapshot."""
+
+from __future__ import annotations
+
+import argparse
+import difflib
+import sys
+from pathlib import Path
+from typing import Iterable
+
+try:
+ import griffe
+except ImportError: # pragma: no cover - exercised only when dependencies are missing.
+ print("griffe is required; install dev dependencies first.", file=sys.stderr)
+ raise
+
+ROOT = Path(__file__).resolve().parents[2]
+SNAPSHOT_PATH = ROOT / "references" / "public_api_snapshot.txt"
+PUBLIC_SPECIAL_NAMES = {"__version__"}
+EXCLUDED_MODULE_PREFIXES = ("posthog.test",)
+
+HEADER = """# This file is generated by .github/scripts/check_public_api.py.
+# Run `make public_api_snapshot` after an intentional public API change.
+# Public API scope: public posthog modules (excluding tests) and their exported
+# members. Modules with __all__ use it; other modules include non-underscore
+# names. External imports are excluded.
+"""
+
+
+def _path_parts(path: str) -> list[str]:
+ return path.split(".")
+
+
+def _is_excluded_path(path: str) -> bool:
+ return any(
+ path == prefix or path.startswith(f"{prefix}.")
+ for prefix in EXCLUDED_MODULE_PREFIXES
+ )
+
+
+def _is_public_name(name: str) -> bool:
+ return not name.startswith("_") or name in PUBLIC_SPECIAL_NAMES
+
+
+def _is_public_posthog_path(path: str) -> bool:
+ if not (path == "posthog" or path.startswith("posthog.")):
+ return False
+ if _is_excluded_path(path):
+ return False
+ return all(_is_public_name(part) for part in _path_parts(path)[1:])
+
+
+def _object_path(obj: object) -> str:
+ return str(getattr(obj, "path", ""))
+
+
+def _alias_target_path(obj: object) -> str:
+ return str(getattr(obj, "target_path", ""))
+
+
+def _is_public_alias(obj: object) -> bool:
+ target_path = _alias_target_path(obj)
+ return _is_public_posthog_path(target_path)
+
+
+def _module_exports(module: object) -> set[str] | None:
+ exports = getattr(module, "exports", None)
+ if exports is None:
+ return None
+ return {str(name) for name in exports}
+
+
+def _is_module(obj: object) -> bool:
+ return bool(getattr(obj, "is_module", False))
+
+
+def _is_class(obj: object) -> bool:
+ return bool(getattr(obj, "is_class", False))
+
+
+def _is_function(obj: object) -> bool:
+ return bool(getattr(obj, "is_function", False))
+
+
+def _is_attribute(obj: object) -> bool:
+ return bool(getattr(obj, "is_attribute", False))
+
+
+def _is_alias(obj: object) -> bool:
+ return bool(getattr(obj, "is_alias", False))
+
+
+def _is_public_member(parent: object, name: str, member: object) -> bool:
+ if name == "__all__":
+ return False
+
+ exports = _module_exports(parent)
+ if exports is not None:
+ if name not in exports:
+ return False
+ elif not _is_public_name(name):
+ return False
+
+ if _is_alias(member):
+ return _is_public_alias(member)
+
+ if _is_module(member):
+ return _is_public_posthog_path(_object_path(member))
+
+ return True
+
+
+def _signature(obj: object) -> str | None:
+ signature = getattr(obj, "signature", None)
+ if signature is None:
+ return None
+
+ try:
+ return str(signature())
+ except Exception: # noqa: BLE001 - keep snapshot generation best-effort.
+ return None
+
+
+def _signature_for_path(obj: object) -> str | None:
+ signature = _signature(obj)
+ if signature is None:
+ return None
+
+ name = str(getattr(obj, "name", ""))
+ if name and signature.startswith(name):
+ return f"{_object_path(obj)}{signature[len(name) :]}"
+ return f"{_object_path(obj)} {signature}"
+
+
+def _attribute_details(obj: object) -> str:
+ parts = [_object_path(obj)]
+
+ annotation = getattr(obj, "annotation", None)
+ if annotation is not None:
+ parts.append(f": {annotation}")
+
+ value = getattr(obj, "value", None)
+ if value is not None:
+ parts.append(f" = {value}")
+
+ return "".join(parts)
+
+
+def _record(obj: object) -> str | None:
+ if _is_alias(obj):
+ return f"alias {_object_path(obj)} -> {_alias_target_path(obj)}"
+
+ if _is_module(obj):
+ return f"module {_object_path(obj)}"
+
+ if _is_class(obj):
+ signature = _signature_for_path(obj)
+ if signature is not None:
+ return f"class {signature}"
+ return f"class {_object_path(obj)}"
+
+ if _is_function(obj):
+ signature = _signature_for_path(obj)
+ prefix = "method" if _is_class(getattr(obj, "parent", None)) else "function"
+ if signature is not None:
+ return f"{prefix} {signature}"
+ return f"{prefix} {_object_path(obj)}"
+
+ if _is_attribute(obj):
+ return f"attribute {_attribute_details(obj)}"
+
+ return f"{getattr(obj, 'kind', 'object')} {_object_path(obj)}"
+
+
+def _iter_class_members(cls: object) -> Iterable[object]:
+ members = getattr(cls, "members", {})
+ for name, member in sorted(members.items()):
+ if not _is_public_member(cls, str(name), member):
+ continue
+
+ yield member
+ if not _is_alias(member) and _is_class(member):
+ yield from _iter_class_members(member)
+
+
+def _iter_module_members(module: object) -> Iterable[object]:
+ yield module
+
+ members = getattr(module, "members", {})
+ for name, member in sorted(members.items()):
+ if _is_alias(member):
+ if _is_public_member(module, str(name), member):
+ yield member
+ continue
+
+ if _is_module(member):
+ if _is_public_posthog_path(_object_path(member)):
+ yield from _iter_module_members(member)
+ continue
+
+ if not _is_public_member(module, str(name), member):
+ continue
+
+ yield member
+ if _is_class(member):
+ yield from _iter_class_members(member)
+
+
+def generate_snapshot() -> str:
+ package = griffe.load(
+ "posthog",
+ allow_inspection=False,
+ search_paths=[ROOT],
+ submodules=True,
+ )
+ records = sorted(
+ {record for obj in _iter_module_members(package) if (record := _record(obj))}
+ )
+ return HEADER + "\n".join(records) + "\n"
+
+
+def check_snapshot(snapshot: str) -> int:
+ try:
+ existing = SNAPSHOT_PATH.read_text()
+ except FileNotFoundError:
+ print(
+ f"Missing public API snapshot: {SNAPSHOT_PATH.relative_to(ROOT)}",
+ file=sys.stderr,
+ )
+ return 1
+
+ if existing == snapshot:
+ print("Public API snapshot is up to date.")
+ return 0
+
+ diff = difflib.unified_diff(
+ existing.splitlines(keepends=True),
+ snapshot.splitlines(keepends=True),
+ fromfile=str(SNAPSHOT_PATH.relative_to(ROOT)),
+ tofile="generated public API",
+ )
+ print(
+ "Public API snapshot is out of date. Run `make public_api_snapshot`.",
+ file=sys.stderr,
+ )
+ print("".join(diff), file=sys.stderr)
+ return 1
+
+
+def main() -> int:
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument(
+ "--write",
+ action="store_true",
+ help="write the generated public API snapshot",
+ )
+ parser.add_argument(
+ "--check",
+ action="store_true",
+ help="check that the public API snapshot is current (default)",
+ )
+ args = parser.parse_args()
+
+ snapshot = generate_snapshot()
+ if args.write:
+ SNAPSHOT_PATH.write_text(snapshot)
+ print(f"Wrote {SNAPSHOT_PATH.relative_to(ROOT)}")
+ return 0
+
+ return check_snapshot(snapshot)
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c0cf09bc..5fccec51 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -62,6 +62,20 @@ jobs:
run: |
mypy --no-site-packages --config-file mypy.ini . | mypy-baseline filter
+ public-api:
+ name: Public API snapshot
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
+
+ - name: Set up Python dev environment
+ uses: ./.github/actions/setup-python-dev
+
+ - name: Check public API snapshot
+ run: |
+ make public_api_check
+
package-build:
name: Package build
runs-on: ubuntu-latest
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 6a3f2459..e5a879e3 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -27,7 +27,6 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: main
- fetch-depth: 0
- name: Check for changesets
id: check
diff --git a/Makefile b/Makefile
index 51fc5a71..c21d4eb2 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,12 @@ test:
coverage run -m pytest
coverage report
+public_api_snapshot:
+ python .github/scripts/check_public_api.py --write
+
+public_api_check:
+ python .github/scripts/check_public_api.py --check
+
build_release:
rm -rf dist/*
python setup.py sdist bdist_wheel
@@ -67,4 +73,4 @@ prep_local:
@echo "Local copy created at ../posthog-python-local"
@echo "Install with: pip install -e ../posthog-python-local"
-.PHONY: test lint build_release build_release_analytics e2e_test prep_local
+.PHONY: test lint public_api_snapshot public_api_check build_release build_release_analytics e2e_test prep_local
diff --git a/pyproject.toml b/pyproject.toml
index 2a46eba0..5775b899 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -42,6 +42,7 @@ otel = [
]
dev = [
"django-stubs",
+ "griffe",
"lxml",
"mypy",
"mypy-baseline",
diff --git a/references/public_api_snapshot.txt b/references/public_api_snapshot.txt
new file mode 100644
index 00000000..cab2d743
--- /dev/null
+++ b/references/public_api_snapshot.txt
@@ -0,0 +1,1193 @@
+# This file is generated by .github/scripts/check_public_api.py.
+# Run `make public_api_snapshot` after an intentional public API change.
+# Public API scope: public posthog modules (excluding tests) and their exported
+# members. Modules with __all__ use it; other modules include non-underscore
+# names. External imports are excluded.
+alias posthog.BeforeSendCallback -> posthog.types.BeforeSendCallback
+alias posthog.Client -> posthog.client.Client
+alias posthog.DEFAULT_CODE_VARIABLES_IGNORE_PATTERNS -> posthog.exception_utils.DEFAULT_CODE_VARIABLES_IGNORE_PATTERNS
+alias posthog.DEFAULT_CODE_VARIABLES_MASK_PATTERNS -> posthog.exception_utils.DEFAULT_CODE_VARIABLES_MASK_PATTERNS
+alias posthog.ExceptionArg -> posthog.args.ExceptionArg
+alias posthog.ExceptionCapture -> posthog.exception_capture.ExceptionCapture
+alias posthog.FeatureFlag -> posthog.types.FeatureFlag
+alias posthog.FeatureFlagEvaluations -> posthog.feature_flag_evaluations.FeatureFlagEvaluations
+alias posthog.FeatureFlagResult -> posthog.types.FeatureFlagResult
+alias posthog.FlagDefinitionCacheData -> posthog.flag_definition_cache.FlagDefinitionCacheData
+alias posthog.FlagDefinitionCacheProvider -> posthog.flag_definition_cache.FlagDefinitionCacheProvider
+alias posthog.FlagsAndPayloads -> posthog.types.FlagsAndPayloads
+alias posthog.InconclusiveMatchError -> posthog.feature_flags.InconclusiveMatchError
+alias posthog.OptionalCaptureArgs -> posthog.args.OptionalCaptureArgs
+alias posthog.OptionalSetArgs -> posthog.args.OptionalSetArgs
+alias posthog.RequiresServerEvaluation -> posthog.feature_flags.RequiresServerEvaluation
+alias posthog.SocketOptions -> posthog.request.SocketOptions
+alias posthog.VERSION -> posthog.version.VERSION
+alias posthog.ai.PromptResult -> posthog.ai.prompts.PromptResult
+alias posthog.ai.PromptSource -> posthog.ai.prompts.PromptSource
+alias posthog.ai.Prompts -> posthog.ai.prompts.Prompts
+alias posthog.ai.anthropic.Anthropic -> posthog.ai.anthropic.anthropic.Anthropic
+alias posthog.ai.anthropic.AnthropicBedrock -> posthog.ai.anthropic.anthropic_providers.AnthropicBedrock
+alias posthog.ai.anthropic.AnthropicVertex -> posthog.ai.anthropic.anthropic_providers.AnthropicVertex
+alias posthog.ai.anthropic.AsyncAnthropic -> posthog.ai.anthropic.anthropic_async.AsyncAnthropic
+alias posthog.ai.anthropic.AsyncAnthropicBedrock -> posthog.ai.anthropic.anthropic_providers.AsyncAnthropicBedrock
+alias posthog.ai.anthropic.AsyncAnthropicVertex -> posthog.ai.anthropic.anthropic_providers.AsyncAnthropicVertex
+alias posthog.ai.anthropic.anthropic.PostHogClient -> posthog.client.Client
+alias posthog.ai.anthropic.anthropic.StreamingContentBlock -> posthog.ai.types.StreamingContentBlock
+alias posthog.ai.anthropic.anthropic.TokenUsage -> posthog.ai.types.TokenUsage
+alias posthog.ai.anthropic.anthropic.ToolInProgress -> posthog.ai.types.ToolInProgress
+alias posthog.ai.anthropic.anthropic.call_llm_and_track_usage -> posthog.ai.utils.call_llm_and_track_usage
+alias posthog.ai.anthropic.anthropic.extract_anthropic_usage_from_event -> posthog.ai.anthropic.anthropic_converter.extract_anthropic_usage_from_event
+alias posthog.ai.anthropic.anthropic.finalize_anthropic_tool_input -> posthog.ai.anthropic.anthropic_converter.finalize_anthropic_tool_input
+alias posthog.ai.anthropic.anthropic.handle_anthropic_content_block_start -> posthog.ai.anthropic.anthropic_converter.handle_anthropic_content_block_start
+alias posthog.ai.anthropic.anthropic.handle_anthropic_text_delta -> posthog.ai.anthropic.anthropic_converter.handle_anthropic_text_delta
+alias posthog.ai.anthropic.anthropic.handle_anthropic_tool_delta -> posthog.ai.anthropic.anthropic_converter.handle_anthropic_tool_delta
+alias posthog.ai.anthropic.anthropic.merge_usage_stats -> posthog.ai.utils.merge_usage_stats
+alias posthog.ai.anthropic.anthropic.sanitize_anthropic -> posthog.ai.sanitization.sanitize_anthropic
+alias posthog.ai.anthropic.anthropic.setup -> posthog.setup
+alias posthog.ai.anthropic.anthropic_async.AsyncStreamWrapper -> posthog.ai.stream.AsyncStreamWrapper
+alias posthog.ai.anthropic.anthropic_async.PostHogClient -> posthog.client.Client
+alias posthog.ai.anthropic.anthropic_async.StreamingContentBlock -> posthog.ai.types.StreamingContentBlock
+alias posthog.ai.anthropic.anthropic_async.TokenUsage -> posthog.ai.types.TokenUsage
+alias posthog.ai.anthropic.anthropic_async.ToolInProgress -> posthog.ai.types.ToolInProgress
+alias posthog.ai.anthropic.anthropic_async.call_llm_and_track_usage_async -> posthog.ai.utils.call_llm_and_track_usage_async
+alias posthog.ai.anthropic.anthropic_async.extract_anthropic_usage_from_event -> posthog.ai.anthropic.anthropic_converter.extract_anthropic_usage_from_event
+alias posthog.ai.anthropic.anthropic_async.finalize_anthropic_tool_input -> posthog.ai.anthropic.anthropic_converter.finalize_anthropic_tool_input
+alias posthog.ai.anthropic.anthropic_async.handle_anthropic_content_block_start -> posthog.ai.anthropic.anthropic_converter.handle_anthropic_content_block_start
+alias posthog.ai.anthropic.anthropic_async.handle_anthropic_text_delta -> posthog.ai.anthropic.anthropic_converter.handle_anthropic_text_delta
+alias posthog.ai.anthropic.anthropic_async.handle_anthropic_tool_delta -> posthog.ai.anthropic.anthropic_converter.handle_anthropic_tool_delta
+alias posthog.ai.anthropic.anthropic_async.merge_usage_stats -> posthog.ai.utils.merge_usage_stats
+alias posthog.ai.anthropic.anthropic_async.sanitize_anthropic -> posthog.ai.sanitization.sanitize_anthropic
+alias posthog.ai.anthropic.anthropic_async.setup -> posthog.setup
+alias posthog.ai.anthropic.anthropic_converter.FormattedContentItem -> posthog.ai.types.FormattedContentItem
+alias posthog.ai.anthropic.anthropic_converter.FormattedFunctionCall -> posthog.ai.types.FormattedFunctionCall
+alias posthog.ai.anthropic.anthropic_converter.FormattedMessage -> posthog.ai.types.FormattedMessage
+alias posthog.ai.anthropic.anthropic_converter.FormattedTextContent -> posthog.ai.types.FormattedTextContent
+alias posthog.ai.anthropic.anthropic_converter.StreamingContentBlock -> posthog.ai.types.StreamingContentBlock
+alias posthog.ai.anthropic.anthropic_converter.TokenUsage -> posthog.ai.types.TokenUsage
+alias posthog.ai.anthropic.anthropic_converter.ToolInProgress -> posthog.ai.types.ToolInProgress
+alias posthog.ai.anthropic.anthropic_converter.serialize_raw_usage -> posthog.ai.utils.serialize_raw_usage
+alias posthog.ai.anthropic.anthropic_providers.AsyncWrappedMessages -> posthog.ai.anthropic.anthropic_async.AsyncWrappedMessages
+alias posthog.ai.anthropic.anthropic_providers.PostHogClient -> posthog.client.Client
+alias posthog.ai.anthropic.anthropic_providers.WrappedMessages -> posthog.ai.anthropic.anthropic.WrappedMessages
+alias posthog.ai.anthropic.anthropic_providers.setup -> posthog.setup
+alias posthog.ai.anthropic.extract_anthropic_tools -> posthog.ai.anthropic.anthropic_converter.extract_anthropic_tools
+alias posthog.ai.anthropic.format_anthropic_input -> posthog.ai.anthropic.anthropic_converter.format_anthropic_input
+alias posthog.ai.anthropic.format_anthropic_response -> posthog.ai.anthropic.anthropic_converter.format_anthropic_response
+alias posthog.ai.anthropic.format_anthropic_streaming_content -> posthog.ai.anthropic.anthropic_converter.format_anthropic_streaming_content
+alias posthog.ai.claude_agent_sdk.PostHogClaudeAgentProcessor -> posthog.ai.claude_agent_sdk.processor.PostHogClaudeAgentProcessor
+alias posthog.ai.claude_agent_sdk.PostHogClaudeSDKClient -> posthog.ai.claude_agent_sdk.client.PostHogClaudeSDKClient
+alias posthog.ai.claude_agent_sdk.client.Client -> posthog.client.Client
+alias posthog.ai.claude_agent_sdk.client.PostHogClaudeAgentProcessor -> posthog.ai.claude_agent_sdk.processor.PostHogClaudeAgentProcessor
+alias posthog.ai.claude_agent_sdk.processor.Client -> posthog.client.Client
+alias posthog.ai.claude_agent_sdk.processor.setup -> posthog.setup
+alias posthog.ai.gemini.AsyncClient -> posthog.ai.gemini.gemini_async.AsyncClient
+alias posthog.ai.gemini.Client -> posthog.ai.gemini.gemini.Client
+alias posthog.ai.gemini.extract_gemini_tools -> posthog.ai.gemini.gemini_converter.extract_gemini_tools
+alias posthog.ai.gemini.format_gemini_input -> posthog.ai.gemini.gemini_converter.format_gemini_input
+alias posthog.ai.gemini.format_gemini_response -> posthog.ai.gemini.gemini_converter.format_gemini_response
+alias posthog.ai.gemini.gemini.PostHogClient -> posthog.client.Client
+alias posthog.ai.gemini.gemini.StreamingEventData -> posthog.ai.types.StreamingEventData
+alias posthog.ai.gemini.gemini.TokenUsage -> posthog.ai.types.TokenUsage
+alias posthog.ai.gemini.gemini.call_llm_and_track_usage -> posthog.ai.utils.call_llm_and_track_usage
+alias posthog.ai.gemini.gemini.capture_streaming_event -> posthog.ai.utils.capture_streaming_event
+alias posthog.ai.gemini.gemini.extract_gemini_content_from_chunk -> posthog.ai.gemini.gemini_converter.extract_gemini_content_from_chunk
+alias posthog.ai.gemini.gemini.extract_gemini_embedding_token_count -> posthog.ai.gemini.gemini_converter.extract_gemini_embedding_token_count
+alias posthog.ai.gemini.gemini.extract_gemini_stop_reason_from_chunk -> posthog.ai.gemini.gemini_converter.extract_gemini_stop_reason_from_chunk
+alias posthog.ai.gemini.gemini.extract_gemini_usage_from_chunk -> posthog.ai.gemini.gemini_converter.extract_gemini_usage_from_chunk
+alias posthog.ai.gemini.gemini.format_gemini_streaming_output -> posthog.ai.gemini.gemini_converter.format_gemini_streaming_output
+alias posthog.ai.gemini.gemini.merge_system_prompt -> posthog.ai.utils.merge_system_prompt
+alias posthog.ai.gemini.gemini.merge_usage_stats -> posthog.ai.utils.merge_usage_stats
+alias posthog.ai.gemini.gemini.sanitize_gemini -> posthog.ai.sanitization.sanitize_gemini
+alias posthog.ai.gemini.gemini.setup -> posthog.setup
+alias posthog.ai.gemini.gemini.with_privacy_mode -> posthog.ai.utils.with_privacy_mode
+alias posthog.ai.gemini.gemini_async.AsyncStreamWrapper -> posthog.ai.stream.AsyncStreamWrapper
+alias posthog.ai.gemini.gemini_async.PostHogClient -> posthog.client.Client
+alias posthog.ai.gemini.gemini_async.StreamingEventData -> posthog.ai.types.StreamingEventData
+alias posthog.ai.gemini.gemini_async.TokenUsage -> posthog.ai.types.TokenUsage
+alias posthog.ai.gemini.gemini_async.call_llm_and_track_usage_async -> posthog.ai.utils.call_llm_and_track_usage_async
+alias posthog.ai.gemini.gemini_async.capture_streaming_event -> posthog.ai.utils.capture_streaming_event
+alias posthog.ai.gemini.gemini_async.extract_gemini_content_from_chunk -> posthog.ai.gemini.gemini_converter.extract_gemini_content_from_chunk
+alias posthog.ai.gemini.gemini_async.extract_gemini_embedding_token_count -> posthog.ai.gemini.gemini_converter.extract_gemini_embedding_token_count
+alias posthog.ai.gemini.gemini_async.extract_gemini_stop_reason_from_chunk -> posthog.ai.gemini.gemini_converter.extract_gemini_stop_reason_from_chunk
+alias posthog.ai.gemini.gemini_async.extract_gemini_usage_from_chunk -> posthog.ai.gemini.gemini_converter.extract_gemini_usage_from_chunk
+alias posthog.ai.gemini.gemini_async.format_gemini_streaming_output -> posthog.ai.gemini.gemini_converter.format_gemini_streaming_output
+alias posthog.ai.gemini.gemini_async.merge_system_prompt -> posthog.ai.utils.merge_system_prompt
+alias posthog.ai.gemini.gemini_async.merge_usage_stats -> posthog.ai.utils.merge_usage_stats
+alias posthog.ai.gemini.gemini_async.sanitize_gemini -> posthog.ai.sanitization.sanitize_gemini
+alias posthog.ai.gemini.gemini_async.setup -> posthog.setup
+alias posthog.ai.gemini.gemini_async.with_privacy_mode -> posthog.ai.utils.with_privacy_mode
+alias posthog.ai.gemini.gemini_converter.FormattedContentItem -> posthog.ai.types.FormattedContentItem
+alias posthog.ai.gemini.gemini_converter.FormattedMessage -> posthog.ai.types.FormattedMessage
+alias posthog.ai.gemini.gemini_converter.TokenUsage -> posthog.ai.types.TokenUsage
+alias posthog.ai.gemini.gemini_converter.serialize_raw_usage -> posthog.ai.utils.serialize_raw_usage
+alias posthog.ai.langchain.CallbackHandler -> posthog.ai.langchain.callbacks.CallbackHandler
+alias posthog.ai.langchain.callbacks.Client -> posthog.client.Client
+alias posthog.ai.langchain.callbacks.get_model_params -> posthog.ai.utils.get_model_params
+alias posthog.ai.langchain.callbacks.sanitize_langchain -> posthog.ai.sanitization.sanitize_langchain
+alias posthog.ai.langchain.callbacks.setup -> posthog.setup
+alias posthog.ai.langchain.callbacks.warn_if_posthog_ai_gateway -> posthog.ai.gateway.warn_if_posthog_ai_gateway
+alias posthog.ai.langchain.callbacks.with_privacy_mode -> posthog.ai.utils.with_privacy_mode
+alias posthog.ai.openai.AsyncAzureOpenAI -> posthog.ai.openai.openai_providers.AsyncAzureOpenAI
+alias posthog.ai.openai.AsyncOpenAI -> posthog.ai.openai.openai_async.AsyncOpenAI
+alias posthog.ai.openai.AzureOpenAI -> posthog.ai.openai.openai_providers.AzureOpenAI
+alias posthog.ai.openai.OpenAI -> posthog.ai.openai.openai.OpenAI
+alias posthog.ai.openai.extract_openai_tools -> posthog.ai.openai.openai_converter.extract_openai_tools
+alias posthog.ai.openai.format_openai_input -> posthog.ai.openai.openai_converter.format_openai_input
+alias posthog.ai.openai.format_openai_response -> posthog.ai.openai.openai_converter.format_openai_response
+alias posthog.ai.openai.format_openai_streaming_content -> posthog.ai.openai.openai_converter.format_openai_streaming_content
+alias posthog.ai.openai.openai.PostHogClient -> posthog.client.Client
+alias posthog.ai.openai.openai.TokenUsage -> posthog.ai.types.TokenUsage
+alias posthog.ai.openai.openai.accumulate_openai_tool_calls -> posthog.ai.openai.openai_converter.accumulate_openai_tool_calls
+alias posthog.ai.openai.openai.call_llm_and_track_usage -> posthog.ai.utils.call_llm_and_track_usage
+alias posthog.ai.openai.openai.extract_available_tool_calls -> posthog.ai.utils.extract_available_tool_calls
+alias posthog.ai.openai.openai.extract_openai_content_from_chunk -> posthog.ai.openai.openai_converter.extract_openai_content_from_chunk
+alias posthog.ai.openai.openai.extract_openai_tool_calls_from_chunk -> posthog.ai.openai.openai_converter.extract_openai_tool_calls_from_chunk
+alias posthog.ai.openai.openai.extract_openai_usage_from_chunk -> posthog.ai.openai.openai_converter.extract_openai_usage_from_chunk
+alias posthog.ai.openai.openai.merge_usage_stats -> posthog.ai.utils.merge_usage_stats
+alias posthog.ai.openai.openai.sanitize_openai -> posthog.ai.sanitization.sanitize_openai
+alias posthog.ai.openai.openai.sanitize_openai_response -> posthog.ai.sanitization.sanitize_openai_response
+alias posthog.ai.openai.openai.setup -> posthog.setup
+alias posthog.ai.openai.openai.with_privacy_mode -> posthog.ai.utils.with_privacy_mode
+alias posthog.ai.openai.openai_async.AsyncStreamWrapper -> posthog.ai.stream.AsyncStreamWrapper
+alias posthog.ai.openai.openai_async.PostHogClient -> posthog.client.Client
+alias posthog.ai.openai.openai_async.TokenUsage -> posthog.ai.types.TokenUsage
+alias posthog.ai.openai.openai_async.accumulate_openai_tool_calls -> posthog.ai.openai.openai_converter.accumulate_openai_tool_calls
+alias posthog.ai.openai.openai_async.call_llm_and_track_usage_async -> posthog.ai.utils.call_llm_and_track_usage_async
+alias posthog.ai.openai.openai_async.extract_available_tool_calls -> posthog.ai.utils.extract_available_tool_calls
+alias posthog.ai.openai.openai_async.extract_openai_content_from_chunk -> posthog.ai.openai.openai_converter.extract_openai_content_from_chunk
+alias posthog.ai.openai.openai_async.extract_openai_tool_calls_from_chunk -> posthog.ai.openai.openai_converter.extract_openai_tool_calls_from_chunk
+alias posthog.ai.openai.openai_async.extract_openai_usage_from_chunk -> posthog.ai.openai.openai_converter.extract_openai_usage_from_chunk
+alias posthog.ai.openai.openai_async.format_openai_streaming_output -> posthog.ai.openai.openai_converter.format_openai_streaming_output
+alias posthog.ai.openai.openai_async.get_model_params -> posthog.ai.utils.get_model_params
+alias posthog.ai.openai.openai_async.merge_usage_stats -> posthog.ai.utils.merge_usage_stats
+alias posthog.ai.openai.openai_async.sanitize_openai -> posthog.ai.sanitization.sanitize_openai
+alias posthog.ai.openai.openai_async.sanitize_openai_response -> posthog.ai.sanitization.sanitize_openai_response
+alias posthog.ai.openai.openai_async.setup -> posthog.setup
+alias posthog.ai.openai.openai_async.with_privacy_mode -> posthog.ai.utils.with_privacy_mode
+alias posthog.ai.openai.openai_converter.FormattedContentItem -> posthog.ai.types.FormattedContentItem
+alias posthog.ai.openai.openai_converter.FormattedFunctionCall -> posthog.ai.types.FormattedFunctionCall
+alias posthog.ai.openai.openai_converter.FormattedImageContent -> posthog.ai.types.FormattedImageContent
+alias posthog.ai.openai.openai_converter.FormattedMessage -> posthog.ai.types.FormattedMessage
+alias posthog.ai.openai.openai_converter.FormattedTextContent -> posthog.ai.types.FormattedTextContent
+alias posthog.ai.openai.openai_converter.TokenUsage -> posthog.ai.types.TokenUsage
+alias posthog.ai.openai.openai_converter.serialize_raw_usage -> posthog.ai.utils.serialize_raw_usage
+alias posthog.ai.openai.openai_providers.AsyncWrappedBeta -> posthog.ai.openai.openai_async.WrappedBeta
+alias posthog.ai.openai.openai_providers.AsyncWrappedChat -> posthog.ai.openai.openai_async.WrappedChat
+alias posthog.ai.openai.openai_providers.AsyncWrappedEmbeddings -> posthog.ai.openai.openai_async.WrappedEmbeddings
+alias posthog.ai.openai.openai_providers.AsyncWrappedResponses -> posthog.ai.openai.openai_async.WrappedResponses
+alias posthog.ai.openai.openai_providers.PostHogClient -> posthog.client.Client
+alias posthog.ai.openai.openai_providers.WrappedBeta -> posthog.ai.openai.openai.WrappedBeta
+alias posthog.ai.openai.openai_providers.WrappedChat -> posthog.ai.openai.openai.WrappedChat
+alias posthog.ai.openai.openai_providers.WrappedEmbeddings -> posthog.ai.openai.openai.WrappedEmbeddings
+alias posthog.ai.openai.openai_providers.WrappedResponses -> posthog.ai.openai.openai.WrappedResponses
+alias posthog.ai.openai.openai_providers.setup -> posthog.setup
+alias posthog.ai.openai_agents.PostHogTracingProcessor -> posthog.ai.openai_agents.processor.PostHogTracingProcessor
+alias posthog.ai.openai_agents.processor.Client -> posthog.client.Client
+alias posthog.ai.openai_agents.processor.setup -> posthog.setup
+alias posthog.ai.otel.PostHogSpanProcessor -> posthog.ai.otel.processor.PostHogSpanProcessor
+alias posthog.ai.otel.PostHogTraceExporter -> posthog.ai.otel.exporter.PostHogTraceExporter
+alias posthog.ai.otel.exporter.DEFAULT_HOST -> posthog.ai.otel.spans.DEFAULT_HOST
+alias posthog.ai.otel.exporter.is_ai_span -> posthog.ai.otel.spans.is_ai_span
+alias posthog.ai.otel.exporter.warn_if_posthog_ai_gateway_otel_attributes -> posthog.ai.gateway.warn_if_posthog_ai_gateway_otel_attributes
+alias posthog.ai.otel.is_ai_span -> posthog.ai.otel.spans.is_ai_span
+alias posthog.ai.otel.processor.DEFAULT_HOST -> posthog.ai.otel.spans.DEFAULT_HOST
+alias posthog.ai.otel.processor.is_ai_span -> posthog.ai.otel.spans.is_ai_span
+alias posthog.ai.otel.processor.warn_if_posthog_ai_gateway_otel_attributes -> posthog.ai.gateway.warn_if_posthog_ai_gateway_otel_attributes
+alias posthog.ai.prompts.USER_AGENT -> posthog.request.USER_AGENT
+alias posthog.ai.prompts.remove_trailing_slash -> posthog.utils.remove_trailing_slash
+alias posthog.ai.utils.FormattedMessage -> posthog.ai.types.FormattedMessage
+alias posthog.ai.utils.PostHogClient -> posthog.client.Client
+alias posthog.ai.utils.StreamingEventData -> posthog.ai.types.StreamingEventData
+alias posthog.ai.utils.TokenUsage -> posthog.ai.types.TokenUsage
+alias posthog.ai.utils.contexts -> posthog.contexts
+alias posthog.ai.utils.get_tags -> posthog.get_tags
+alias posthog.ai.utils.identify_context -> posthog.identify_context
+alias posthog.ai.utils.new_context -> posthog.new_context
+alias posthog.ai.utils.sanitize_anthropic -> posthog.ai.sanitization.sanitize_anthropic
+alias posthog.ai.utils.sanitize_gemini -> posthog.ai.sanitization.sanitize_gemini
+alias posthog.ai.utils.sanitize_langchain -> posthog.ai.sanitization.sanitize_langchain
+alias posthog.ai.utils.sanitize_openai -> posthog.ai.sanitization.sanitize_openai
+alias posthog.ai.utils.tag -> posthog.tag
+alias posthog.ai.utils.warn_if_posthog_ai_gateway -> posthog.ai.gateway.warn_if_posthog_ai_gateway
+alias posthog.args.FeatureFlagEvaluations -> posthog.feature_flag_evaluations.FeatureFlagEvaluations
+alias posthog.args.SendFeatureFlagsOptions -> posthog.types.SendFeatureFlagsOptions
+alias posthog.client.AI_EVENTS_ENDPOINT -> posthog.request.AI_EVENTS_ENDPOINT
+alias posthog.client.APIError -> posthog.request.APIError
+alias posthog.client.Consumer -> posthog.consumer.Consumer
+alias posthog.client.DEFAULT_CODE_VARIABLES_IGNORE_PATTERNS -> posthog.exception_utils.DEFAULT_CODE_VARIABLES_IGNORE_PATTERNS
+alias posthog.client.DEFAULT_CODE_VARIABLES_MASK_PATTERNS -> posthog.exception_utils.DEFAULT_CODE_VARIABLES_MASK_PATTERNS
+alias posthog.client.EVENTS_ENDPOINT -> posthog.request.EVENTS_ENDPOINT
+alias posthog.client.ExceptionArg -> posthog.args.ExceptionArg
+alias posthog.client.ExceptionCapture -> posthog.exception_capture.ExceptionCapture
+alias posthog.client.FeatureFlag -> posthog.types.FeatureFlag
+alias posthog.client.FeatureFlagError -> posthog.types.FeatureFlagError
+alias posthog.client.FeatureFlagEvaluations -> posthog.feature_flag_evaluations.FeatureFlagEvaluations
+alias posthog.client.FeatureFlagResult -> posthog.types.FeatureFlagResult
+alias posthog.client.FlagCache -> posthog.utils.FlagCache
+alias posthog.client.FlagDefinitionCacheData -> posthog.flag_definition_cache.FlagDefinitionCacheData
+alias posthog.client.FlagDefinitionCacheProvider -> posthog.flag_definition_cache.FlagDefinitionCacheProvider
+alias posthog.client.FlagMetadata -> posthog.types.FlagMetadata
+alias posthog.client.FlagValue -> posthog.types.FlagValue
+alias posthog.client.FlagsAndPayloads -> posthog.types.FlagsAndPayloads
+alias posthog.client.FlagsResponse -> posthog.types.FlagsResponse
+alias posthog.client.ID_TYPES -> posthog.args.ID_TYPES
+alias posthog.client.InconclusiveMatchError -> posthog.feature_flags.InconclusiveMatchError
+alias posthog.client.OptionalCaptureArgs -> posthog.args.OptionalCaptureArgs
+alias posthog.client.OptionalSetArgs -> posthog.args.OptionalSetArgs
+alias posthog.client.Poller -> posthog.poller.Poller
+alias posthog.client.QuotaLimitError -> posthog.request.QuotaLimitError
+alias posthog.client.RedisFlagCache -> posthog.utils.RedisFlagCache
+alias posthog.client.RequestsConnectionError -> posthog.request.RequestsConnectionError
+alias posthog.client.RequestsTimeout -> posthog.request.RequestsTimeout
+alias posthog.client.RequiresServerEvaluation -> posthog.feature_flags.RequiresServerEvaluation
+alias posthog.client.SendFeatureFlagsOptions -> posthog.types.SendFeatureFlagsOptions
+alias posthog.client.SizeLimitedDict -> posthog.utils.SizeLimitedDict
+alias posthog.client.VERSION -> posthog.version.VERSION
+alias posthog.client.batch_post -> posthog.request.batch_post
+alias posthog.client.clean -> posthog.utils.clean
+alias posthog.client.determine_server_host -> posthog.request.determine_server_host
+alias posthog.client.exc_info_from_error -> posthog.exception_utils.exc_info_from_error
+alias posthog.client.exception_is_already_captured -> posthog.exception_utils.exception_is_already_captured
+alias posthog.client.exceptions_from_error_tuple -> posthog.exception_utils.exceptions_from_error_tuple
+alias posthog.client.flags -> posthog.request.flags
+alias posthog.client.get -> posthog.request.get
+alias posthog.client.get_capture_exception_code_variables_context -> posthog.contexts.get_capture_exception_code_variables_context
+alias posthog.client.get_code_variables_ignore_patterns_context -> posthog.contexts.get_code_variables_ignore_patterns_context
+alias posthog.client.get_code_variables_mask_patterns_context -> posthog.contexts.get_code_variables_mask_patterns_context
+alias posthog.client.get_context_device_id -> posthog.contexts.get_context_device_id
+alias posthog.client.get_context_distinct_id -> posthog.contexts.get_context_distinct_id
+alias posthog.client.get_context_session_id -> posthog.contexts.get_context_session_id
+alias posthog.client.guess_timezone -> posthog.utils.guess_timezone
+alias posthog.client.handle_in_app -> posthog.exception_utils.handle_in_app
+alias posthog.client.is_ai_event -> posthog.request.is_ai_event
+alias posthog.client.mark_exception_as_captured -> posthog.exception_utils.mark_exception_as_captured
+alias posthog.client.match_feature_flag_properties -> posthog.feature_flags.match_feature_flag_properties
+alias posthog.client.new_context -> posthog.contexts.new_context
+alias posthog.client.normalize_flags_response -> posthog.types.normalize_flags_response
+alias posthog.client.normalize_host -> posthog.request.normalize_host
+alias posthog.client.remote_config -> posthog.request.remote_config
+alias posthog.client.reset_sessions -> posthog.request.reset_sessions
+alias posthog.client.resolve_bucketing_value -> posthog.feature_flags.resolve_bucketing_value
+alias posthog.client.system_context -> posthog.utils.system_context
+alias posthog.client.to_flags_and_payloads -> posthog.types.to_flags_and_payloads
+alias posthog.client.to_payloads -> posthog.types.to_payloads
+alias posthog.client.to_values -> posthog.types.to_values
+alias posthog.client.try_attach_code_variables_to_frames -> posthog.exception_utils.try_attach_code_variables_to_frames
+alias posthog.consumer.AI_EVENTS_ENDPOINT -> posthog.request.AI_EVENTS_ENDPOINT
+alias posthog.consumer.APIError -> posthog.request.APIError
+alias posthog.consumer.DatetimeSerializer -> posthog.request.DatetimeSerializer
+alias posthog.consumer.EVENTS_ENDPOINT -> posthog.request.EVENTS_ENDPOINT
+alias posthog.consumer.batch_post -> posthog.request.batch_post
+alias posthog.consumer.is_ai_event -> posthog.request.is_ai_event
+alias posthog.contexts.Client -> posthog.client.Client
+alias posthog.disable_connection_reuse -> posthog.request.disable_connection_reuse
+alias posthog.enable_keep_alive -> posthog.request.enable_keep_alive
+alias posthog.exception_capture.BucketedRateLimiter -> posthog.bucketed_rate_limiter.BucketedRateLimiter
+alias posthog.exception_capture.Client -> posthog.client.Client
+alias posthog.exception_capture.walk_exception_chain -> posthog.exception_utils.walk_exception_chain
+alias posthog.exception_utils.ExcInfo -> posthog.args.ExcInfo
+alias posthog.exception_utils.ExceptionArg -> posthog.args.ExceptionArg
+alias posthog.feature_flag_evaluations.FlagValue -> posthog.types.FlagValue
+alias posthog.feature_flags.FlagValue -> posthog.types.FlagValue
+alias posthog.feature_flags.convert_to_datetime_aware -> posthog.utils.convert_to_datetime_aware
+alias posthog.feature_flags.is_valid_regex -> posthog.utils.is_valid_regex
+alias posthog.feature_flags.utils -> posthog.utils
+alias posthog.inner_get_tags -> posthog.contexts.get_tags
+alias posthog.inner_identify_context -> posthog.contexts.identify_context
+alias posthog.inner_new_context -> posthog.contexts.new_context
+alias posthog.inner_scoped -> posthog.contexts.scoped
+alias posthog.inner_set_capture_exception_code_variables_context -> posthog.contexts.set_capture_exception_code_variables_context
+alias posthog.inner_set_code_variables_ignore_patterns_context -> posthog.contexts.set_code_variables_ignore_patterns_context
+alias posthog.inner_set_code_variables_mask_patterns_context -> posthog.contexts.set_code_variables_mask_patterns_context
+alias posthog.inner_set_context_device_id -> posthog.contexts.set_context_device_id
+alias posthog.inner_set_context_session -> posthog.contexts.set_context_session
+alias posthog.inner_tag -> posthog.contexts.tag
+alias posthog.integrations.django.Client -> posthog.client.Client
+alias posthog.integrations.django.contexts -> posthog.contexts
+alias posthog.request.VERSION -> posthog.version.VERSION
+alias posthog.request.remove_trailing_slash -> posthog.utils.remove_trailing_slash
+alias posthog.set_socket_options -> posthog.request.set_socket_options
+attribute posthog.__version__ = VERSION
+attribute posthog.ai.anthropic.anthropic.Anthropic.messages = WrappedMessages(self)
+attribute posthog.ai.anthropic.anthropic_async.AsyncAnthropic.messages = AsyncWrappedMessages(self)
+attribute posthog.ai.anthropic.anthropic_providers.AnthropicBedrock.messages = WrappedMessages(self)
+attribute posthog.ai.anthropic.anthropic_providers.AnthropicVertex.messages = WrappedMessages(self)
+attribute posthog.ai.anthropic.anthropic_providers.AsyncAnthropicBedrock.messages = AsyncWrappedMessages(self)
+attribute posthog.ai.anthropic.anthropic_providers.AsyncAnthropicVertex.messages = AsyncWrappedMessages(self)
+attribute posthog.ai.claude_agent_sdk.client.log = logging.getLogger('posthog')
+attribute posthog.ai.claude_agent_sdk.processor.log = logging.getLogger('posthog')
+attribute posthog.ai.gateway.POSTHOG_AI_GATEWAY_HOSTS = ['gateway.posthog.com', 'gateway.us.posthog.com', 'gateway.eu.posthog.com', 'ai-gateway.us.posthog.com', 'ai-gateway.eu.posthog.com']
+attribute posthog.ai.gateway.log = logging.getLogger('posthog')
+attribute posthog.ai.gemini.gemini.Client.models = Models(api_key=api_key, vertexai=vertexai, credentials=credentials, project=project, location=location, debug_config=debug_config, http_options=http_options, posthog_client=(self._ph_client), posthog_distinct_id=posthog_distinct_id, posthog_properties=posthog_properties, posthog_privacy_mode=posthog_privacy_mode, posthog_groups=posthog_groups, **kwargs)
+attribute posthog.ai.gemini.gemini_async.AsyncClient.models = AsyncModels(api_key=api_key, vertexai=vertexai, credentials=credentials, project=project, location=location, debug_config=debug_config, http_options=http_options, posthog_client=(self._ph_client), posthog_distinct_id=posthog_distinct_id, posthog_properties=posthog_properties, posthog_privacy_mode=posthog_privacy_mode, posthog_groups=posthog_groups, **kwargs)
+attribute posthog.ai.gemini.gemini_converter.GeminiMessage.content: Union[str, List[Any]]
+attribute posthog.ai.gemini.gemini_converter.GeminiMessage.parts: List[Union[GeminiPart, Dict[str, Any]]]
+attribute posthog.ai.gemini.gemini_converter.GeminiMessage.role: str
+attribute posthog.ai.gemini.gemini_converter.GeminiMessage.text: str
+attribute posthog.ai.gemini.gemini_converter.GeminiPart.text: str
+attribute posthog.ai.gemini.genai = _GenAI()
+attribute posthog.ai.langchain.callbacks.GenerationMetadata.base_url: Optional[str] = None
+attribute posthog.ai.langchain.callbacks.GenerationMetadata.model: Optional[str] = None
+attribute posthog.ai.langchain.callbacks.GenerationMetadata.model_params: Optional[Dict[str, Any]] = None
+attribute posthog.ai.langchain.callbacks.GenerationMetadata.posthog_properties: Optional[Dict[str, Any]] = None
+attribute posthog.ai.langchain.callbacks.GenerationMetadata.provider: Optional[str] = None
+attribute posthog.ai.langchain.callbacks.GenerationMetadata.tools: Optional[List[Dict[str, Any]]] = None
+attribute posthog.ai.langchain.callbacks.ModelUsage.cache_read_tokens: Optional[int]
+attribute posthog.ai.langchain.callbacks.ModelUsage.cache_write_tokens: Optional[int]
+attribute posthog.ai.langchain.callbacks.ModelUsage.input_tokens: Optional[int]
+attribute posthog.ai.langchain.callbacks.ModelUsage.output_tokens: Optional[int]
+attribute posthog.ai.langchain.callbacks.ModelUsage.reasoning_tokens: Optional[int]
+attribute posthog.ai.langchain.callbacks.RunMetadata = Union[SpanMetadata, GenerationMetadata]
+attribute posthog.ai.langchain.callbacks.RunMetadataStorage = Dict[UUID, RunMetadata]
+attribute posthog.ai.langchain.callbacks.SpanMetadata.end_time: Optional[float]
+attribute posthog.ai.langchain.callbacks.SpanMetadata.input: Optional[Any]
+attribute posthog.ai.langchain.callbacks.SpanMetadata.latency: float
+attribute posthog.ai.langchain.callbacks.SpanMetadata.name: str
+attribute posthog.ai.langchain.callbacks.SpanMetadata.start_time: float
+attribute posthog.ai.langchain.callbacks.log = logging.getLogger('posthog')
+attribute posthog.ai.openai.openai.OpenAI.beta = WrappedBeta(self, self._original_beta)
+attribute posthog.ai.openai.openai.OpenAI.chat = WrappedChat(self, self._original_chat)
+attribute posthog.ai.openai.openai.OpenAI.embeddings = WrappedEmbeddings(self, self._original_embeddings)
+attribute posthog.ai.openai.openai.OpenAI.responses = WrappedResponses(self, self._original_responses)
+attribute posthog.ai.openai.openai.WrappedBeta.chat
+attribute posthog.ai.openai.openai.WrappedBetaChat.completions
+attribute posthog.ai.openai.openai.WrappedChat.completions
+attribute posthog.ai.openai.openai_async.AsyncOpenAI.beta = WrappedBeta(self, self._original_beta)
+attribute posthog.ai.openai.openai_async.AsyncOpenAI.chat = WrappedChat(self, self._original_chat)
+attribute posthog.ai.openai.openai_async.AsyncOpenAI.embeddings = WrappedEmbeddings(self, self._original_embeddings)
+attribute posthog.ai.openai.openai_async.AsyncOpenAI.responses = WrappedResponses(self, self._original_responses)
+attribute posthog.ai.openai.openai_async.WrappedBeta.chat
+attribute posthog.ai.openai.openai_async.WrappedBetaChat.completions
+attribute posthog.ai.openai.openai_async.WrappedChat.completions
+attribute posthog.ai.openai.openai_providers.AsyncAzureOpenAI.beta = AsyncWrappedBeta(self, self._original_beta)
+attribute posthog.ai.openai.openai_providers.AsyncAzureOpenAI.chat = AsyncWrappedChat(self, self._original_chat)
+attribute posthog.ai.openai.openai_providers.AsyncAzureOpenAI.embeddings = AsyncWrappedEmbeddings(self, self._original_embeddings)
+attribute posthog.ai.openai.openai_providers.AsyncAzureOpenAI.responses = AsyncWrappedResponses(self, self._original_responses)
+attribute posthog.ai.openai.openai_providers.AzureOpenAI.beta = WrappedBeta(self, self._original_beta)
+attribute posthog.ai.openai.openai_providers.AzureOpenAI.chat = WrappedChat(self, self._original_chat)
+attribute posthog.ai.openai.openai_providers.AzureOpenAI.embeddings = WrappedEmbeddings(self, self._original_embeddings)
+attribute posthog.ai.openai.openai_providers.AzureOpenAI.responses = WrappedResponses(self, self._original_responses)
+attribute posthog.ai.openai.wrapper_utils.log = logging.getLogger('posthog')
+attribute posthog.ai.openai_agents.processor.log = logging.getLogger('posthog')
+attribute posthog.ai.otel.spans.AI_SPAN_PREFIXES = ('gen_ai.', 'llm.', 'ai.', 'traceloop.')
+attribute posthog.ai.otel.spans.DEFAULT_HOST = 'https://us.i.posthog.com'
+attribute posthog.ai.prompts.APP_ENDPOINT = 'https://us.posthog.com'
+attribute posthog.ai.prompts.CachedPrompt.fetched_at = fetched_at
+attribute posthog.ai.prompts.CachedPrompt.name = name
+attribute posthog.ai.prompts.CachedPrompt.prompt = prompt
+attribute posthog.ai.prompts.CachedPrompt.version = version
+attribute posthog.ai.prompts.DEFAULT_CACHE_TTL_SECONDS = 300
+attribute posthog.ai.prompts.PromptCacheKey = tuple[str, Optional[int]]
+attribute posthog.ai.prompts.PromptResult.name: Optional[str] = None
+attribute posthog.ai.prompts.PromptResult.prompt: str
+attribute posthog.ai.prompts.PromptResult.source: PromptSource
+attribute posthog.ai.prompts.PromptResult.version: Optional[int] = None
+attribute posthog.ai.prompts.PromptSource = Literal['api', 'cache', 'stale_cache', 'code_fallback']
+attribute posthog.ai.prompts.PromptVariables = Dict[str, Union[str, int, float, bool]]
+attribute posthog.ai.prompts.log = logging.getLogger('posthog')
+attribute posthog.ai.sanitization.REDACTED_IMAGE_PLACEHOLDER = '[base64 image redacted]'
+attribute posthog.ai.stream.T = TypeVar('T')
+attribute posthog.ai.types.FormattedContentItem = Union[FormattedTextContent, FormattedFunctionCall, FormattedImageContent, Dict[str, Any]]
+attribute posthog.ai.types.FormattedFunctionCall.function: Dict[str, Any]
+attribute posthog.ai.types.FormattedFunctionCall.id: Optional[str]
+attribute posthog.ai.types.FormattedFunctionCall.type: str
+attribute posthog.ai.types.FormattedImageContent.image: str
+attribute posthog.ai.types.FormattedImageContent.type: str
+attribute posthog.ai.types.FormattedMessage.content: Union[str, List[FormattedContentItem], Any]
+attribute posthog.ai.types.FormattedMessage.role: str
+attribute posthog.ai.types.FormattedTextContent.text: str
+attribute posthog.ai.types.FormattedTextContent.type: str
+attribute posthog.ai.types.ProviderResponse.error: Optional[str]
+attribute posthog.ai.types.ProviderResponse.messages: List[FormattedMessage]
+attribute posthog.ai.types.ProviderResponse.usage: TokenUsage
+attribute posthog.ai.types.StreamingContentBlock.function: Optional[Dict[str, Any]]
+attribute posthog.ai.types.StreamingContentBlock.id: Optional[str]
+attribute posthog.ai.types.StreamingContentBlock.text: Optional[str]
+attribute posthog.ai.types.StreamingContentBlock.type: str
+attribute posthog.ai.types.StreamingEventData.base_url: str
+attribute posthog.ai.types.StreamingEventData.distinct_id: Optional[str]
+attribute posthog.ai.types.StreamingEventData.formatted_input: Any
+attribute posthog.ai.types.StreamingEventData.formatted_output: Any
+attribute posthog.ai.types.StreamingEventData.groups: Optional[Dict[str, Any]]
+attribute posthog.ai.types.StreamingEventData.kwargs: Dict[str, Any]
+attribute posthog.ai.types.StreamingEventData.latency: float
+attribute posthog.ai.types.StreamingEventData.model: str
+attribute posthog.ai.types.StreamingEventData.privacy_mode: bool
+attribute posthog.ai.types.StreamingEventData.properties: Optional[Dict[str, Any]]
+attribute posthog.ai.types.StreamingEventData.provider: str
+attribute posthog.ai.types.StreamingEventData.stop_reason: Optional[str]
+attribute posthog.ai.types.StreamingEventData.trace_id: Optional[str]
+attribute posthog.ai.types.StreamingEventData.usage_stats: TokenUsage
+attribute posthog.ai.types.TokenUsage.cache_creation_input_tokens: Optional[int]
+attribute posthog.ai.types.TokenUsage.cache_read_input_tokens: Optional[int]
+attribute posthog.ai.types.TokenUsage.input_tokens: int
+attribute posthog.ai.types.TokenUsage.output_tokens: int
+attribute posthog.ai.types.TokenUsage.raw_usage: Optional[Any]
+attribute posthog.ai.types.TokenUsage.reasoning_tokens: Optional[int]
+attribute posthog.ai.types.TokenUsage.web_search_count: Optional[int]
+attribute posthog.ai.types.ToolInProgress.block: StreamingContentBlock
+attribute posthog.ai.types.ToolInProgress.input_string: str
+attribute posthog.api_key = None
+attribute posthog.args.ExcInfo = Union[Tuple[Type[BaseException], BaseException, Optional[TracebackType]], Tuple[None, None, None]]
+attribute posthog.args.ExceptionArg = Union[BaseException, ExcInfo]
+attribute posthog.args.ID_TYPES = Union[numbers.Number, str, UUID, int]
+attribute posthog.args.OptionalCaptureArgs.disable_geoip: NotRequired[Optional[bool]]
+attribute posthog.args.OptionalCaptureArgs.distinct_id: NotRequired[Optional[ID_TYPES]]
+attribute posthog.args.OptionalCaptureArgs.flags: NotRequired[Optional[FeatureFlagEvaluations]]
+attribute posthog.args.OptionalCaptureArgs.groups: NotRequired[Optional[Dict[str, str]]]
+attribute posthog.args.OptionalCaptureArgs.properties: NotRequired[Optional[Dict[str, Any]]]
+attribute posthog.args.OptionalCaptureArgs.send_feature_flags: NotRequired[Optional[Union[bool, SendFeatureFlagsOptions]]]
+attribute posthog.args.OptionalCaptureArgs.timestamp: NotRequired[Optional[Union[datetime, str]]]
+attribute posthog.args.OptionalCaptureArgs.uuid: NotRequired[Optional[str]]
+attribute posthog.args.OptionalSetArgs.disable_geoip: NotRequired[Optional[bool]]
+attribute posthog.args.OptionalSetArgs.distinct_id: NotRequired[Optional[ID_TYPES]]
+attribute posthog.args.OptionalSetArgs.properties: NotRequired[Optional[Dict[str, Any]]]
+attribute posthog.args.OptionalSetArgs.timestamp: NotRequired[Optional[Union[datetime, str]]]
+attribute posthog.args.OptionalSetArgs.uuid: NotRequired[Optional[str]]
+attribute posthog.before_send = None
+attribute posthog.bucketed_rate_limiter.Number = Union[int, float]
+attribute posthog.bucketed_rate_limiter.ONE_DAY_IN_SECONDS = 86400.0
+attribute posthog.bucketed_rate_limiter.log = logging.getLogger('posthog')
+attribute posthog.capture_exception_code_variables = False
+attribute posthog.client.Client.api_key = (project_api_key or '').strip()
+attribute posthog.client.Client.capture_exception_code_variables = capture_exception_code_variables
+attribute posthog.client.Client.code_variables_ignore_patterns = code_variables_ignore_patterns if code_variables_ignore_patterns is not None else DEFAULT_CODE_VARIABLES_IGNORE_PATTERNS
+attribute posthog.client.Client.code_variables_mask_patterns = code_variables_mask_patterns if code_variables_mask_patterns is not None else DEFAULT_CODE_VARIABLES_MASK_PATTERNS
+attribute posthog.client.Client.cohorts: Optional[dict[str, Any]] = None
+attribute posthog.client.Client.consumers = None
+attribute posthog.client.Client.debug = debug
+attribute posthog.client.Client.disable_geoip = disable_geoip
+attribute posthog.client.Client.disabled = disabled or not self.api_key
+attribute posthog.client.Client.distinct_ids_feature_flags_reported = SizeLimitedDict(MAX_DICT_SIZE, set)
+attribute posthog.client.Client.enable_exception_autocapture = enable_exception_autocapture
+attribute posthog.client.Client.enable_exception_autocapture_rate_limiting = enable_exception_autocapture_rate_limiting
+attribute posthog.client.Client.enable_local_evaluation = enable_local_evaluation
+attribute posthog.client.Client.exception_autocapture_bucket_size = exception_autocapture_bucket_size
+attribute posthog.client.Client.exception_autocapture_refill_interval_seconds = exception_autocapture_refill_interval_seconds
+attribute posthog.client.Client.exception_autocapture_refill_rate = exception_autocapture_refill_rate
+attribute posthog.client.Client.exception_capture = None
+attribute posthog.client.Client.feature_flags
+attribute posthog.client.Client.feature_flags_by_key: Optional[dict[str, Any]] = None
+attribute posthog.client.Client.feature_flags_request_timeout_seconds = feature_flags_request_timeout_seconds
+attribute posthog.client.Client.flag_cache = self._initialize_flag_cache(flag_fallback_cache_url)
+attribute posthog.client.Client.flag_definition_version = 0
+attribute posthog.client.Client.flag_fallback_cache_url = flag_fallback_cache_url
+attribute posthog.client.Client.group_type_mapping: Optional[dict[str, str]] = None
+attribute posthog.client.Client.gzip = gzip
+attribute posthog.client.Client.historical_migration = historical_migration
+attribute posthog.client.Client.host = determine_server_host(host)
+attribute posthog.client.Client.in_app_modules = in_app_modules
+attribute posthog.client.Client.is_server = is_server
+attribute posthog.client.Client.log = logging.getLogger('posthog')
+attribute posthog.client.Client.log_captured_exceptions = log_captured_exceptions
+attribute posthog.client.Client.on_error = on_error
+attribute posthog.client.Client.personal_api_key = (personal_api_key.strip() if isinstance(personal_api_key, str) else personal_api_key) or None
+attribute posthog.client.Client.poll_interval = poll_interval
+attribute posthog.client.Client.poller: Optional[Poller] = None
+attribute posthog.client.Client.privacy_mode = privacy_mode
+attribute posthog.client.Client.project_root = project_root
+attribute posthog.client.Client.queue: Queue = Queue(max_queue_size)
+attribute posthog.client.Client.raw_host = normalize_host(host)
+attribute posthog.client.Client.send = send
+attribute posthog.client.Client.super_properties = super_properties
+attribute posthog.client.Client.sync_mode = sync_mode
+attribute posthog.client.Client.timeout = timeout
+attribute posthog.client.MAX_DICT_SIZE = 50000
+attribute posthog.code_variables_ignore_patterns = DEFAULT_CODE_VARIABLES_IGNORE_PATTERNS
+attribute posthog.code_variables_mask_patterns = DEFAULT_CODE_VARIABLES_MASK_PATTERNS
+attribute posthog.consumer.AI_MAX_MSG_SIZE = 8 * 1024 * 1024
+attribute posthog.consumer.BATCH_SIZE_LIMIT = 5 * 1024 * 1024
+attribute posthog.consumer.Consumer.api_key = api_key
+attribute posthog.consumer.Consumer.daemon = True
+attribute posthog.consumer.Consumer.dedicated_ai_endpoint = dedicated_ai_endpoint
+attribute posthog.consumer.Consumer.flush_at = flush_at
+attribute posthog.consumer.Consumer.flush_interval = flush_interval
+attribute posthog.consumer.Consumer.gzip = gzip
+attribute posthog.consumer.Consumer.historical_migration = historical_migration
+attribute posthog.consumer.Consumer.host = host
+attribute posthog.consumer.Consumer.log = logging.getLogger('posthog')
+attribute posthog.consumer.Consumer.on_error = on_error
+attribute posthog.consumer.Consumer.queue = queue
+attribute posthog.consumer.Consumer.retries = retries
+attribute posthog.consumer.Consumer.running = True
+attribute posthog.consumer.Consumer.timeout = timeout
+attribute posthog.consumer.MAX_MSG_SIZE = 900 * 1024
+attribute posthog.contexts.ContextScope.capture_exception_code_variables: Optional[bool] = None
+attribute posthog.contexts.ContextScope.capture_exceptions = capture_exceptions
+attribute posthog.contexts.ContextScope.client: Optional[Client] = client
+attribute posthog.contexts.ContextScope.code_variables_ignore_patterns: Optional[list] = None
+attribute posthog.contexts.ContextScope.code_variables_mask_patterns: Optional[list] = None
+attribute posthog.contexts.ContextScope.device_id: Optional[str] = None
+attribute posthog.contexts.ContextScope.distinct_id: Optional[str] = None
+attribute posthog.contexts.ContextScope.fresh = fresh
+attribute posthog.contexts.ContextScope.parent = parent
+attribute posthog.contexts.ContextScope.session_id: Optional[str] = None
+attribute posthog.contexts.ContextScope.tags: Dict[str, Any] = {}
+attribute posthog.contexts.F = TypeVar('F', bound=(Callable[..., Any]))
+attribute posthog.debug = False
+attribute posthog.default_client = None
+attribute posthog.disable_geoip = True
+attribute posthog.disabled = False
+attribute posthog.enable_exception_autocapture = False
+attribute posthog.enable_exception_autocapture_rate_limiting = False
+attribute posthog.enable_local_evaluation = True
+attribute posthog.exception_autocapture_bucket_size = ExceptionCapture.DEFAULT_BUCKET_SIZE
+attribute posthog.exception_autocapture_refill_interval_seconds = ExceptionCapture.DEFAULT_REFILL_INTERVAL_SECONDS
+attribute posthog.exception_autocapture_refill_rate = ExceptionCapture.DEFAULT_REFILL_RATE
+attribute posthog.exception_capture.ExceptionCapture.DEFAULT_BUCKET_SIZE = 50
+attribute posthog.exception_capture.ExceptionCapture.DEFAULT_REFILL_INTERVAL_SECONDS = 10
+attribute posthog.exception_capture.ExceptionCapture.DEFAULT_REFILL_RATE = 10
+attribute posthog.exception_capture.ExceptionCapture.client = client
+attribute posthog.exception_capture.ExceptionCapture.log = logging.getLogger('posthog')
+attribute posthog.exception_capture.ExceptionCapture.original_excepthook = sys.excepthook
+attribute posthog.exception_utils.Annotated = Union[AnnotatedValue, T]
+attribute posthog.exception_utils.AnnotatedValue.metadata = metadata
+attribute posthog.exception_utils.AnnotatedValue.value = value
+attribute posthog.exception_utils.BASE64_ALPHABET = re.compile('^[a-zA-Z0-9/+=]*$')
+attribute posthog.exception_utils.CODE_VARIABLES_REDACTED_VALUE = '$$_posthog_redacted_based_on_masking_rules_$$'
+attribute posthog.exception_utils.CODE_VARIABLES_TOO_LONG_VALUE = '$$_posthog_value_too_long_$$'
+attribute posthog.exception_utils.DEFAULT_CODE_VARIABLES_IGNORE_PATTERNS = ['^__.*']
+attribute posthog.exception_utils.DEFAULT_CODE_VARIABLES_MASK_PATTERNS = ['(?i)password', '(?i)secret', '(?i)passwd', '(?i)pwd', '(?i)api_key', '(?i)apikey', '(?i)auth', '(?i)credentials', '(?i)privatekey', '(?i)private_key', '(?i)token', '(?i)aws_access_key_id', '(?i)_pass', '(?i)sk_', '(?i)jwt']
+attribute posthog.exception_utils.DEFAULT_MAX_VALUE_LENGTH = 1024
+attribute posthog.exception_utils.DEFAULT_TOTAL_VARIABLES_SIZE_LIMIT = 20 * 1024
+attribute posthog.exception_utils.Event = TypedDict('Event', {'breadcrumbs': Dict[Literal['values'], List[Dict[str, Any]]], 'check_in_id': str, 'contexts': Dict[str, Dict[str, object]], 'dist': str, 'duration': Optional[float], 'environment': str, 'errors': List[Dict[str, Any]], 'event_id': str, 'exception': Dict[Literal['values'], List[Dict[str, Any]]], 'level': LogLevelStr, 'logger': str, 'message': str, 'modules': Dict[str, str], 'monitor_slug': Optional[str], 'platform': Literal['python'], 'profile': object, 'release': str, 'request': Dict[str, object], 'server_name': str, 'spans': List[Dict[str, object]], 'stacktrace': Dict[str, object], 'start_timestamp': datetime, 'status': Optional[str], 'threads': Dict[Literal['values'], List[Dict[str, Any]]], 'timestamp': Optional[datetime], 'transaction': str, 'type': Literal['check_in', 'transaction'], 'user': Dict[str, object], '_metrics_summary': Dict[str, object]}, total=False)
+attribute posthog.exception_utils.HAS_CHAINED_EXCEPTIONS = hasattr(Exception, '__suppress_context__')
+attribute posthog.exception_utils.LogLevelStr = Literal['fatal', 'critical', 'error', 'warning', 'info', 'debug']
+attribute posthog.exception_utils.SENSITIVE_DATA_SUBSTITUTE = '[Filtered]'
+attribute posthog.exception_utils.T = TypeVar('T')
+attribute posthog.exception_utils.VariableSizeLimiter.current_size = 0
+attribute posthog.exception_utils.VariableSizeLimiter.max_size = max_size
+attribute posthog.exception_utils.epoch = datetime(1970, 1, 1)
+attribute posthog.feature_flag_evaluations.FeatureFlagEvaluations.keys: List[str]
+attribute posthog.feature_flags.ConditionMatch.MATCH = 'match'
+attribute posthog.feature_flags.ConditionMatch.NO_MATCH = 'no_match'
+attribute posthog.feature_flags.ConditionMatch.OUT_OF_ROLLOUT_BOUND = 'out_of_rollout_bound'
+attribute posthog.feature_flags.DATE_OPERATORS = ('is_date_before', 'is_date_after')
+attribute posthog.feature_flags.EQUALITY_OPERATORS = ('exact', 'is_not', 'is_set', 'is_not_set')
+attribute posthog.feature_flags.NONE_VALUES_ALLOWED_OPERATORS = ['is_not']
+attribute posthog.feature_flags.NUMERIC_OPERATORS = ('gt', 'gte', 'lt', 'lte')
+attribute posthog.feature_flags.PROPERTY_OPERATORS = EQUALITY_OPERATORS + STRING_OPERATORS + NUMERIC_OPERATORS + DATE_OPERATORS + SEMVER_OPERATORS
+attribute posthog.feature_flags.SEMVER_COMPARISON_OPERATORS = ('semver_eq', 'semver_neq', 'semver_gt', 'semver_gte', 'semver_lt', 'semver_lte')
+attribute posthog.feature_flags.SEMVER_OPERATORS = SEMVER_COMPARISON_OPERATORS + SEMVER_RANGE_OPERATORS
+attribute posthog.feature_flags.SEMVER_RANGE_OPERATORS = ('semver_tilde', 'semver_caret', 'semver_wildcard')
+attribute posthog.feature_flags.STRING_OPERATORS = ('icontains', 'not_icontains', 'regex', 'not_regex')
+attribute posthog.feature_flags.log = logging.getLogger('posthog')
+attribute posthog.feature_flags_request_timeout_seconds = 3
+attribute posthog.flag_definition_cache.FlagDefinitionCacheData.cohorts: Required[Dict[str, Any]]
+attribute posthog.flag_definition_cache.FlagDefinitionCacheData.flags: Required[List[Dict[str, Any]]]
+attribute posthog.flag_definition_cache.FlagDefinitionCacheData.group_type_mapping: Required[Dict[str, str]]
+attribute posthog.flag_definition_cache_provider = None
+attribute posthog.host = None
+attribute posthog.in_app_modules = None
+attribute posthog.integrations.celery.PosthogCeleryIntegration.capture_exceptions = capture_exceptions
+attribute posthog.integrations.celery.PosthogCeleryIntegration.capture_task_lifecycle_events = capture_task_lifecycle_events
+attribute posthog.integrations.celery.PosthogCeleryIntegration.client = client
+attribute posthog.integrations.celery.PosthogCeleryIntegration.propagate_context = propagate_context
+attribute posthog.integrations.celery.PosthogCeleryIntegration.task_filter = task_filter
+attribute posthog.integrations.django.PosthogContextMiddleware.async_capable = True
+attribute posthog.integrations.django.PosthogContextMiddleware.capture_exceptions = settings.POSTHOG_MW_CAPTURE_EXCEPTIONS
+attribute posthog.integrations.django.PosthogContextMiddleware.client = cast('Optional[Client]', settings.POSTHOG_MW_CLIENT)
+attribute posthog.integrations.django.PosthogContextMiddleware.extra_tags = cast('Optional[Callable[[HttpRequest], Dict[str, Any]]]', settings.POSTHOG_MW_EXTRA_TAGS)
+attribute posthog.integrations.django.PosthogContextMiddleware.get_response = get_response
+attribute posthog.integrations.django.PosthogContextMiddleware.request_filter = cast('Optional[Callable[[HttpRequest], bool]]', settings.POSTHOG_MW_REQUEST_FILTER)
+attribute posthog.integrations.django.PosthogContextMiddleware.sync_capable = True
+attribute posthog.integrations.django.PosthogContextMiddleware.tag_map = cast('Optional[Callable[[Dict[str, Any]], Dict[str, Any]]]', settings.POSTHOG_MW_TAG_MAP)
+attribute posthog.is_server = True
+attribute posthog.log_captured_exceptions = False
+attribute posthog.on_error = None
+attribute posthog.personal_api_key = None
+attribute posthog.poll_interval = 30
+attribute posthog.poller.Poller.args = args
+attribute posthog.poller.Poller.daemon = True
+attribute posthog.poller.Poller.execute = execute
+attribute posthog.poller.Poller.interval = interval
+attribute posthog.poller.Poller.kwargs = kwargs
+attribute posthog.poller.Poller.stopped = threading.Event()
+attribute posthog.privacy_mode = False
+attribute posthog.project_api_key = None
+attribute posthog.project_root = None
+attribute posthog.request.AI_EVENTS_ENDPOINT = '/i/v0/ai/batch/'
+attribute posthog.request.APIError.message = message
+attribute posthog.request.APIError.retry_after = retry_after
+attribute posthog.request.APIError.status = status
+attribute posthog.request.DEFAULT_HOST = US_INGESTION_ENDPOINT
+attribute posthog.request.EU_INGESTION_ENDPOINT = 'https://eu.i.posthog.com'
+attribute posthog.request.EVENTS_ENDPOINT = '/batch/'
+attribute posthog.request.GetResponse.data: Any
+attribute posthog.request.GetResponse.etag: Optional[str] = None
+attribute posthog.request.GetResponse.not_modified: bool = False
+attribute posthog.request.HTTPAdapterWithSocketOptions.socket_options = socket_options
+attribute posthog.request.KEEPALIVE_IDLE_SECONDS = 60
+attribute posthog.request.KEEPALIVE_INTERVAL_SECONDS = 60
+attribute posthog.request.KEEPALIVE_PROBE_COUNT = 3
+attribute posthog.request.KEEP_ALIVE_SOCKET_OPTIONS: SocketOptions = list(HTTPConnection.default_socket_options) + [(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)]
+attribute posthog.request.RETRY_STATUS_FORCELIST = [408, 500, 502, 503, 504]
+attribute posthog.request.RequestsConnectionError = requests.exceptions.ConnectionError
+attribute posthog.request.RequestsTimeout = requests.exceptions.Timeout
+attribute posthog.request.SocketOptions = List[Tuple[int, int, Union[int, bytes]]]
+attribute posthog.request.USER_AGENT = 'posthog-python/' + VERSION
+attribute posthog.request.US_INGESTION_ENDPOINT = 'https://us.i.posthog.com'
+attribute posthog.send = True
+attribute posthog.super_properties = None
+attribute posthog.sync_mode = False
+attribute posthog.types.BeforeSendCallback = Callable[[dict[str, Any]], Optional[dict[str, Any]]]
+attribute posthog.types.FeatureFlag.enabled: bool
+attribute posthog.types.FeatureFlag.key: str
+attribute posthog.types.FeatureFlag.metadata: Union[FlagMetadata, LegacyFlagMetadata]
+attribute posthog.types.FeatureFlag.reason: Optional[FlagReason]
+attribute posthog.types.FeatureFlag.variant: Optional[str]
+attribute posthog.types.FeatureFlagError.CONNECTION_ERROR = 'connection_error'
+attribute posthog.types.FeatureFlagError.ERRORS_WHILE_COMPUTING = 'errors_while_computing_flags'
+attribute posthog.types.FeatureFlagError.FLAG_MISSING = 'flag_missing'
+attribute posthog.types.FeatureFlagError.QUOTA_LIMITED = 'quota_limited'
+attribute posthog.types.FeatureFlagError.TIMEOUT = 'timeout'
+attribute posthog.types.FeatureFlagError.UNKNOWN_ERROR = 'unknown_error'
+attribute posthog.types.FeatureFlagResult.enabled: bool
+attribute posthog.types.FeatureFlagResult.key: str
+attribute posthog.types.FeatureFlagResult.payload: Optional[Any]
+attribute posthog.types.FeatureFlagResult.reason: Optional[str]
+attribute posthog.types.FeatureFlagResult.variant: Optional[str]
+attribute posthog.types.FlagMetadata.description: str
+attribute posthog.types.FlagMetadata.id: int
+attribute posthog.types.FlagMetadata.payload: Optional[str]
+attribute posthog.types.FlagMetadata.version: int
+attribute posthog.types.FlagReason.code: str
+attribute posthog.types.FlagReason.condition_index: Optional[int]
+attribute posthog.types.FlagReason.description: str
+attribute posthog.types.FlagValue = Union[bool, str]
+attribute posthog.types.FlagsAndPayloads.featureFlagPayloads: Optional[dict[str, Any]]
+attribute posthog.types.FlagsAndPayloads.featureFlags: Optional[dict[str, FlagValue]]
+attribute posthog.types.FlagsResponse.errorsWhileComputingFlags: bool
+attribute posthog.types.FlagsResponse.evaluatedAt: Optional[int]
+attribute posthog.types.FlagsResponse.flags: dict[str, FeatureFlag]
+attribute posthog.types.FlagsResponse.quotaLimit: Optional[List[str]]
+attribute posthog.types.FlagsResponse.requestId: str
+attribute posthog.types.LegacyFlagMetadata.payload: Any
+attribute posthog.types.SendFeatureFlagsOptions.flag_keys_filter: Optional[list[str]]
+attribute posthog.types.SendFeatureFlagsOptions.group_properties: Optional[dict[str, dict[str, Any]]]
+attribute posthog.types.SendFeatureFlagsOptions.only_evaluate_locally: Optional[bool]
+attribute posthog.types.SendFeatureFlagsOptions.person_properties: Optional[dict[str, Any]]
+attribute posthog.types.SendFeatureFlagsOptions.should_send: bool
+attribute posthog.utils.CACHE_KEY_PREFIX = 'posthog:flags:'
+attribute posthog.utils.CACHE_MAX_SIZE = 10000
+attribute posthog.utils.CACHE_STALE_TTL = 3600
+attribute posthog.utils.CACHE_TTL = 300
+attribute posthog.utils.FlagCache.access_times = {}
+attribute posthog.utils.FlagCache.cache = {}
+attribute posthog.utils.FlagCache.default_ttl = default_ttl
+attribute posthog.utils.FlagCache.max_size = max_size
+attribute posthog.utils.FlagCacheEntry.flag_definition_version = flag_definition_version
+attribute posthog.utils.FlagCacheEntry.flag_result = flag_result
+attribute posthog.utils.FlagCacheEntry.timestamp = timestamp or time.time()
+attribute posthog.utils.RedisFlagCache.default_ttl = default_ttl
+attribute posthog.utils.RedisFlagCache.key_prefix = key_prefix
+attribute posthog.utils.RedisFlagCache.redis = redis_client
+attribute posthog.utils.RedisFlagCache.stale_ttl = stale_ttl
+attribute posthog.utils.RedisFlagCache.version_key = f'{key_prefix}version'
+attribute posthog.utils.SizeLimitedDict.max_size = max_size
+attribute posthog.utils.log = logging.getLogger('posthog')
+attribute posthog.version.VERSION = '7.19.1'
+class posthog.Posthog
+class posthog.ai.anthropic.anthropic.Anthropic(posthog_client: Optional[PostHogClient] = None, **kwargs)
+class posthog.ai.anthropic.anthropic.WrappedMessages
+class posthog.ai.anthropic.anthropic_async.AsyncAnthropic(posthog_client: Optional[PostHogClient] = None, **kwargs)
+class posthog.ai.anthropic.anthropic_async.AsyncWrappedMessages
+class posthog.ai.anthropic.anthropic_providers.AnthropicBedrock(posthog_client: Optional[PostHogClient] = None, **kwargs)
+class posthog.ai.anthropic.anthropic_providers.AnthropicVertex(posthog_client: Optional[PostHogClient] = None, **kwargs)
+class posthog.ai.anthropic.anthropic_providers.AsyncAnthropicBedrock(posthog_client: Optional[PostHogClient] = None, **kwargs)
+class posthog.ai.anthropic.anthropic_providers.AsyncAnthropicVertex(posthog_client: Optional[PostHogClient] = None, **kwargs)
+class posthog.ai.claude_agent_sdk.client.PostHogClaudeSDKClient(options: Optional[ClaudeAgentOptions] = None, transport: Any = None, *, posthog_client: Optional[Client] = None, posthog_distinct_id: Optional[Union[str, Callable[[ResultMessage], Optional[str]]]] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: bool = False, posthog_groups: Optional[Dict[str, Any]] = None)
+class posthog.ai.claude_agent_sdk.processor.PostHogClaudeAgentProcessor(client: Optional[Client] = None, distinct_id: Optional[Union[str, Callable[[ResultMessage], Optional[str]]]] = None, privacy_mode: bool = False, groups: Optional[Dict[str, Any]] = None, properties: Optional[Dict[str, Any]] = None)
+class posthog.ai.gemini.gemini.Client(api_key: Optional[str] = None, vertexai: Optional[bool] = None, credentials: Optional[Any] = None, project: Optional[str] = None, location: Optional[str] = None, debug_config: Optional[Any] = None, http_options: Optional[Any] = None, posthog_client: Optional[PostHogClient] = None, posthog_distinct_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: bool = False, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs)
+class posthog.ai.gemini.gemini.Models(api_key: Optional[str] = None, vertexai: Optional[bool] = None, credentials: Optional[Any] = None, project: Optional[str] = None, location: Optional[str] = None, debug_config: Optional[Any] = None, http_options: Optional[Any] = None, posthog_client: Optional[PostHogClient] = None, posthog_distinct_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: bool = False, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs)
+class posthog.ai.gemini.gemini_async.AsyncClient(api_key: Optional[str] = None, vertexai: Optional[bool] = None, credentials: Optional[Any] = None, project: Optional[str] = None, location: Optional[str] = None, debug_config: Optional[Any] = None, http_options: Optional[Any] = None, posthog_client: Optional[PostHogClient] = None, posthog_distinct_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: bool = False, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs)
+class posthog.ai.gemini.gemini_async.AsyncModels(api_key: Optional[str] = None, vertexai: Optional[bool] = None, credentials: Optional[Any] = None, project: Optional[str] = None, location: Optional[str] = None, debug_config: Optional[Any] = None, http_options: Optional[Any] = None, posthog_client: Optional[PostHogClient] = None, posthog_distinct_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: bool = False, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs)
+class posthog.ai.gemini.gemini_converter.GeminiMessage
+class posthog.ai.gemini.gemini_converter.GeminiPart
+class posthog.ai.langchain.callbacks.CallbackHandler(client: Optional[Client] = None, *, distinct_id: Optional[Union[str, int, UUID]] = None, trace_id: Optional[Union[str, int, float, UUID]] = None, properties: Optional[Dict[str, Any]] = None, privacy_mode: bool = False, groups: Optional[Dict[str, Any]] = None)
+class posthog.ai.langchain.callbacks.GenerationMetadata(name: str, start_time: float, end_time: Optional[float], input: Optional[Any], provider: Optional[str] = None, model: Optional[str] = None, model_params: Optional[Dict[str, Any]] = None, base_url: Optional[str] = None, tools: Optional[List[Dict[str, Any]]] = None, posthog_properties: Optional[Dict[str, Any]] = None)
+class posthog.ai.langchain.callbacks.ModelUsage(input_tokens: Optional[int], output_tokens: Optional[int], cache_write_tokens: Optional[int], cache_read_tokens: Optional[int], reasoning_tokens: Optional[int])
+class posthog.ai.langchain.callbacks.SpanMetadata(name: str, start_time: float, end_time: Optional[float], input: Optional[Any])
+class posthog.ai.openai.openai.OpenAI(posthog_client: Optional[PostHogClient] = None, **kwargs)
+class posthog.ai.openai.openai.WrappedBeta
+class posthog.ai.openai.openai.WrappedBetaChat
+class posthog.ai.openai.openai.WrappedBetaCompletions
+class posthog.ai.openai.openai.WrappedChat
+class posthog.ai.openai.openai.WrappedCompletions
+class posthog.ai.openai.openai.WrappedEmbeddings
+class posthog.ai.openai.openai.WrappedResponses
+class posthog.ai.openai.openai_async.AsyncOpenAI(posthog_client: Optional[PostHogClient] = None, **kwargs)
+class posthog.ai.openai.openai_async.WrappedBeta
+class posthog.ai.openai.openai_async.WrappedBetaChat
+class posthog.ai.openai.openai_async.WrappedBetaCompletions
+class posthog.ai.openai.openai_async.WrappedChat
+class posthog.ai.openai.openai_async.WrappedCompletions
+class posthog.ai.openai.openai_async.WrappedEmbeddings
+class posthog.ai.openai.openai_async.WrappedResponses
+class posthog.ai.openai.openai_providers.AsyncAzureOpenAI(posthog_client: Optional[PostHogClient] = None, **kwargs)
+class posthog.ai.openai.openai_providers.AzureOpenAI(posthog_client: Optional[PostHogClient] = None, **kwargs)
+class posthog.ai.openai_agents.processor.PostHogTracingProcessor(client: Optional[Client] = None, distinct_id: Optional[Union[str, Callable[[Trace], Optional[str]]]] = None, privacy_mode: bool = False, groups: Optional[Dict[str, Any]] = None, properties: Optional[Dict[str, Any]] = None)
+class posthog.ai.otel.exporter.PostHogTraceExporter(api_key: str, host: str = DEFAULT_HOST)
+class posthog.ai.otel.processor.PostHogSpanProcessor(api_key: str, host: str = DEFAULT_HOST)
+class posthog.ai.prompts.CachedPrompt(prompt: str, fetched_at: float, name: str, version: int)
+class posthog.ai.prompts.PromptResult(source: PromptSource, prompt: str, name: Optional[str] = None, version: Optional[int] = None)
+class posthog.ai.prompts.Prompts(posthog: Optional[Any] = None, *, personal_api_key: Optional[str] = None, project_api_key: Optional[str] = None, host: Optional[str] = None, default_cache_ttl_seconds: Optional[int] = None, capture_errors: bool = False)
+class posthog.ai.stream.AsyncStreamWrapper(generator: AsyncGenerator[T, None], stream: Optional[Any] = None)
+class posthog.ai.types.FormattedFunctionCall
+class posthog.ai.types.FormattedImageContent
+class posthog.ai.types.FormattedMessage
+class posthog.ai.types.FormattedTextContent
+class posthog.ai.types.ProviderResponse
+class posthog.ai.types.StreamingContentBlock
+class posthog.ai.types.StreamingEventData
+class posthog.ai.types.TokenUsage
+class posthog.ai.types.ToolInProgress
+class posthog.args.OptionalCaptureArgs
+class posthog.args.OptionalSetArgs
+class posthog.bucketed_rate_limiter.BucketedRateLimiter(bucket_size: Number, refill_rate: Number, refill_interval_seconds: Number, on_bucket_rate_limited: Optional[Callable[[Hashable], None]] = None, clock: Callable[[], float] = time.monotonic)
+class posthog.client.Client(project_api_key: str, host=None, debug=False, max_queue_size=10000, send=True, on_error=None, flush_at=100, flush_interval=0.5, gzip=False, max_retries=3, sync_mode=False, timeout=15, thread=1, poll_interval=30, personal_api_key=None, disabled=False, disable_geoip=True, is_server=True, historical_migration=False, feature_flags_request_timeout_seconds=3, super_properties=None, enable_exception_autocapture=False, log_captured_exceptions=False, project_root=None, privacy_mode=False, before_send=None, flag_fallback_cache_url=None, enable_local_evaluation=True, flag_definition_cache_provider: Optional[FlagDefinitionCacheProvider] = None, capture_exception_code_variables=False, code_variables_mask_patterns=None, code_variables_ignore_patterns=None, in_app_modules: list[str] | None = None, enable_exception_autocapture_rate_limiting=False, exception_autocapture_bucket_size=ExceptionCapture.DEFAULT_BUCKET_SIZE, exception_autocapture_refill_rate=ExceptionCapture.DEFAULT_REFILL_RATE, exception_autocapture_refill_interval_seconds=ExceptionCapture.DEFAULT_REFILL_INTERVAL_SECONDS, _dedicated_ai_endpoint=False)
+class posthog.consumer.Consumer(queue, api_key, flush_at=100, host=None, on_error=None, flush_interval=0.5, gzip=False, retries=10, timeout=15, historical_migration=False, dedicated_ai_endpoint=False)
+class posthog.contexts.ContextScope(parent=None, fresh: bool = False, capture_exceptions: bool = True, client: Optional[Client] = None)
+class posthog.exception_capture.ExceptionCapture(client: Client, rate_limiting_enabled=False, bucket_size=DEFAULT_BUCKET_SIZE, refill_rate=DEFAULT_REFILL_RATE, refill_interval_seconds=DEFAULT_REFILL_INTERVAL_SECONDS)
+class posthog.exception_utils.AnnotatedValue(value, metadata)
+class posthog.exception_utils.VariableSizeLimiter(max_size=DEFAULT_TOTAL_VARIABLES_SIZE_LIMIT)
+class posthog.feature_flag_evaluations.FeatureFlagEvaluations(host: _FeatureFlagEvaluationsHost, distinct_id: str, flags: Dict[str, _EvaluatedFlagRecord], groups: Optional[Dict[str, str]] = None, disable_geoip: Optional[bool] = None, request_id: Optional[str] = None, evaluated_at: Optional[int] = None, errors_while_computing: bool = False, quota_limited: bool = False, accessed: Optional[Set[str]] = None)
+class posthog.feature_flags.ConditionMatch
+class posthog.feature_flags.InconclusiveMatchError
+class posthog.feature_flags.RequiresServerEvaluation
+class posthog.flag_definition_cache.FlagDefinitionCacheData
+class posthog.flag_definition_cache.FlagDefinitionCacheProvider
+class posthog.integrations.celery.PosthogCeleryIntegration(client: Optional[Client] = None, capture_exceptions: bool = True, capture_task_lifecycle_events: bool = True, propagate_context: bool = True, task_filter: Optional[Callable[[Optional[str], dict[str, Any]], bool]] = None)
+class posthog.integrations.django.PosthogContextMiddleware(get_response)
+class posthog.poller.Poller(interval, execute, *args, **kwargs)
+class posthog.request.APIError(status: Union[int, str], message: str, retry_after: Optional[float] = None)
+class posthog.request.DatetimeSerializer
+class posthog.request.GetResponse(data: Any, etag: Optional[str] = None, not_modified: bool = False)
+class posthog.request.HTTPAdapterWithSocketOptions(*args, socket_options: Optional[SocketOptions] = None, **kwargs)
+class posthog.request.QuotaLimitError
+class posthog.types.FeatureFlag(key: str, enabled: bool, variant: Optional[str], reason: Optional[FlagReason], metadata: Union[FlagMetadata, LegacyFlagMetadata])
+class posthog.types.FeatureFlagError
+class posthog.types.FeatureFlagResult(key: str, enabled: bool, variant: Optional[str], payload: Optional[Any], reason: Optional[str])
+class posthog.types.FlagMetadata(id: int, payload: Optional[str], version: int, description: str)
+class posthog.types.FlagReason(code: str, condition_index: Optional[int], description: str)
+class posthog.types.FlagsAndPayloads
+class posthog.types.FlagsResponse
+class posthog.types.LegacyFlagMetadata(payload: Any)
+class posthog.types.SendFeatureFlagsOptions
+class posthog.utils.FlagCache(max_size=CACHE_MAX_SIZE, default_ttl=CACHE_TTL)
+class posthog.utils.FlagCacheEntry(flag_result, flag_definition_version, timestamp=None)
+class posthog.utils.RedisFlagCache(redis_client, default_ttl=CACHE_TTL, stale_ttl=CACHE_STALE_TTL, key_prefix=CACHE_KEY_PREFIX)
+class posthog.utils.SizeLimitedDict(max_size, *args, **kwargs)
+function posthog.ai.anthropic.anthropic_converter.extract_anthropic_stop_reason(response: Any) -> Optional[str]
+function posthog.ai.anthropic.anthropic_converter.extract_anthropic_tools(kwargs: Dict[str, Any]) -> Optional[Any]
+function posthog.ai.anthropic.anthropic_converter.extract_anthropic_usage_from_event(event: Any) -> TokenUsage
+function posthog.ai.anthropic.anthropic_converter.extract_anthropic_usage_from_response(response: Any) -> TokenUsage
+function posthog.ai.anthropic.anthropic_converter.extract_anthropic_web_search_count(response: Any) -> int
+function posthog.ai.anthropic.anthropic_converter.finalize_anthropic_tool_input(event: Any, content_blocks: List[StreamingContentBlock], tools_in_progress: Dict[str, ToolInProgress]) -> None
+function posthog.ai.anthropic.anthropic_converter.format_anthropic_input(messages: List[Dict[str, Any]], system: Optional[str] = None) -> List[FormattedMessage]
+function posthog.ai.anthropic.anthropic_converter.format_anthropic_response(response: Any) -> List[FormattedMessage]
+function posthog.ai.anthropic.anthropic_converter.format_anthropic_streaming_content(content_blocks: List[StreamingContentBlock]) -> List[FormattedContentItem]
+function posthog.ai.anthropic.anthropic_converter.format_anthropic_streaming_input(kwargs: Dict[str, Any]) -> Any
+function posthog.ai.anthropic.anthropic_converter.format_anthropic_streaming_output_complete(content_blocks: List[StreamingContentBlock], accumulated_content: str) -> List[FormattedMessage]
+function posthog.ai.anthropic.anthropic_converter.handle_anthropic_content_block_start(event: Any) -> Tuple[Optional[StreamingContentBlock], Optional[ToolInProgress]]
+function posthog.ai.anthropic.anthropic_converter.handle_anthropic_text_delta(event: Any, current_block: Optional[StreamingContentBlock]) -> Optional[str]
+function posthog.ai.anthropic.anthropic_converter.handle_anthropic_tool_delta(event: Any, content_blocks: List[StreamingContentBlock], tools_in_progress: Dict[str, ToolInProgress]) -> None
+function posthog.ai.claude_agent_sdk.instrument(client: Optional[Client] = None, distinct_id: Optional[Union[str, Callable[[ResultMessage], Optional[str]]]] = None, privacy_mode: bool = False, groups: Optional[Dict[str, Any]] = None, properties: Optional[Dict[str, Any]] = None) -> PostHogClaudeAgentProcessor
+function posthog.ai.claude_agent_sdk.query(*, prompt: Any, options: Optional[ClaudeAgentOptions] = None, transport: Any = None, posthog_client: Optional[Client] = None, posthog_distinct_id: Optional[Union[str, Callable[[ResultMessage], Optional[str]]]] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: bool = False, posthog_groups: Optional[Dict[str, Any]] = None)
+function posthog.ai.gateway.is_posthog_ai_gateway_url(base_url: Any) -> bool
+function posthog.ai.gateway.warn_if_posthog_ai_gateway(base_url: Any) -> None
+function posthog.ai.gateway.warn_if_posthog_ai_gateway_otel_attributes(attributes: Optional[Mapping[str, Any]]) -> None
+function posthog.ai.gemini.gemini_converter.extract_gemini_content_from_chunk(chunk: Any) -> Optional[Dict[str, Any]]
+function posthog.ai.gemini.gemini_converter.extract_gemini_embedding_token_count(response) -> int
+function posthog.ai.gemini.gemini_converter.extract_gemini_stop_reason(response: Any) -> Optional[str]
+function posthog.ai.gemini.gemini_converter.extract_gemini_stop_reason_from_chunk(chunk: Any) -> Optional[str]
+function posthog.ai.gemini.gemini_converter.extract_gemini_system_instruction(config: Any) -> Optional[str]
+function posthog.ai.gemini.gemini_converter.extract_gemini_tools(kwargs: Dict[str, Any]) -> Optional[Any]
+function posthog.ai.gemini.gemini_converter.extract_gemini_usage_from_chunk(chunk: Any) -> TokenUsage
+function posthog.ai.gemini.gemini_converter.extract_gemini_usage_from_response(response: Any) -> TokenUsage
+function posthog.ai.gemini.gemini_converter.extract_gemini_web_search_count(response: Any) -> int
+function posthog.ai.gemini.gemini_converter.format_gemini_input(contents: Any) -> List[FormattedMessage]
+function posthog.ai.gemini.gemini_converter.format_gemini_input_with_system(contents: Any, config: Any = None) -> List[FormattedMessage]
+function posthog.ai.gemini.gemini_converter.format_gemini_response(response: Any) -> List[FormattedMessage]
+function posthog.ai.gemini.gemini_converter.format_gemini_streaming_output(accumulated_content: Union[str, List[Any]]) -> List[FormattedMessage]
+function posthog.ai.openai.openai_converter.accumulate_openai_tool_calls(accumulated_tool_calls: Dict[int, Dict[str, Any]], chunk_tool_calls: List[Dict[str, Any]]) -> None
+function posthog.ai.openai.openai_converter.extract_openai_content_from_chunk(chunk: Any, provider_type: str = 'chat') -> Optional[str]
+function posthog.ai.openai.openai_converter.extract_openai_stop_reason(response: Any) -> Optional[str]
+function posthog.ai.openai.openai_converter.extract_openai_tool_calls_from_chunk(chunk: Any) -> Optional[List[Dict[str, Any]]]
+function posthog.ai.openai.openai_converter.extract_openai_tools(kwargs: Dict[str, Any]) -> Optional[Any]
+function posthog.ai.openai.openai_converter.extract_openai_usage_from_chunk(chunk: Any, provider_type: str = 'chat') -> TokenUsage
+function posthog.ai.openai.openai_converter.extract_openai_usage_from_response(response: Any) -> TokenUsage
+function posthog.ai.openai.openai_converter.extract_openai_web_search_count(response: Any) -> int
+function posthog.ai.openai.openai_converter.format_openai_input(messages: Optional[List[Dict[str, Any]]] = None, input_data: Optional[Any] = None) -> List[FormattedMessage]
+function posthog.ai.openai.openai_converter.format_openai_response(response: Any) -> List[FormattedMessage]
+function posthog.ai.openai.openai_converter.format_openai_streaming_content(accumulated_content: str, tool_calls: Optional[List[Dict[str, Any]]] = None) -> List[FormattedContentItem]
+function posthog.ai.openai.openai_converter.format_openai_streaming_input(kwargs: Dict[str, Any], api_type: str = 'chat') -> Any
+function posthog.ai.openai.openai_converter.format_openai_streaming_output(accumulated_content: Any, provider_type: str = 'chat', tool_calls: Optional[List[Dict[str, Any]]] = None) -> List[FormattedMessage]
+function posthog.ai.openai.wrapper_utils.reset_fallback_warnings() -> None
+function posthog.ai.openai.wrapper_utils.warn_on_fallback(wrapper_name: str, name: str) -> None
+function posthog.ai.openai_agents.instrument(client: Optional[Client] = None, distinct_id: Optional[Union[str, Callable[[Trace], Optional[str]]]] = None, privacy_mode: bool = False, groups: Optional[Dict[str, Any]] = None, properties: Optional[Dict[str, Any]] = None) -> PostHogTracingProcessor
+function posthog.ai.otel.spans.is_ai_span(span: ReadableSpan) -> bool
+function posthog.ai.sanitization.is_base64_data_url(text: str) -> bool
+function posthog.ai.sanitization.is_raw_base64(text: str) -> bool
+function posthog.ai.sanitization.is_valid_url(text: str) -> bool
+function posthog.ai.sanitization.process_gemini_item(item: Any) -> Any
+function posthog.ai.sanitization.process_messages(messages: Any, transform_content_func) -> Any
+function posthog.ai.sanitization.redact_base64_data_url(value: Any) -> Any
+function posthog.ai.sanitization.sanitize_anthropic(data: Any) -> Any
+function posthog.ai.sanitization.sanitize_anthropic_image(item: Any) -> Any
+function posthog.ai.sanitization.sanitize_gemini(data: Any) -> Any
+function posthog.ai.sanitization.sanitize_gemini_part(part: Any) -> Any
+function posthog.ai.sanitization.sanitize_langchain(data: Any) -> Any
+function posthog.ai.sanitization.sanitize_langchain_image(item: Any) -> Any
+function posthog.ai.sanitization.sanitize_openai(data: Any) -> Any
+function posthog.ai.sanitization.sanitize_openai_image(item: Any) -> Any
+function posthog.ai.sanitization.sanitize_openai_response(data: Any) -> Any
+function posthog.ai.sanitization.sanitize_openai_response_image(item: Any) -> Any
+function posthog.ai.utils.call_llm_and_track_usage(posthog_distinct_id: Optional[str], ph_client: PostHogClient, provider: str, posthog_trace_id: Optional[str], posthog_properties: Optional[Dict[str, Any]], posthog_privacy_mode: bool, posthog_groups: Optional[Dict[str, Any]], base_url: str, call_method: Callable[..., Any], **kwargs: Any) -> Any
+function posthog.ai.utils.call_llm_and_track_usage_async(posthog_distinct_id: Optional[str], ph_client: PostHogClient, provider: str, posthog_trace_id: Optional[str], posthog_properties: Optional[Dict[str, Any]], posthog_privacy_mode: bool, posthog_groups: Optional[Dict[str, Any]], base_url: str, call_async_method: Callable[..., Any], **kwargs: Any) -> Any
+function posthog.ai.utils.capture_streaming_event(ph_client: PostHogClient, event_data: StreamingEventData)
+function posthog.ai.utils.extract_available_tool_calls(provider: str, kwargs: Dict[str, Any])
+function posthog.ai.utils.extract_stop_reason(response: Any, provider: str) -> Optional[str]
+function posthog.ai.utils.format_response(response, provider: str)
+function posthog.ai.utils.get_model_params(kwargs: Dict[str, Any]) -> Dict[str, Any]
+function posthog.ai.utils.get_usage(response, provider: str) -> TokenUsage
+function posthog.ai.utils.merge_system_prompt(kwargs: Dict[str, Any], provider: str) -> List[FormattedMessage]
+function posthog.ai.utils.merge_usage_stats(target: TokenUsage, source: TokenUsage, mode: str = 'incremental') -> None
+function posthog.ai.utils.sanitize_messages(data: Any, provider: str) -> Any
+function posthog.ai.utils.serialize_raw_usage(raw_usage: Any) -> Optional[Dict[str, Any]]
+function posthog.ai.utils.with_privacy_mode(ph_client: PostHogClient, privacy_mode: bool, value: Any)
+function posthog.alias(previous_id, distinct_id, timestamp=None, uuid=None, disable_geoip=None)
+function posthog.capture(event: str, **kwargs: Unpack[OptionalCaptureArgs]) -> Optional[str]
+function posthog.capture_exception(exception: Optional[ExceptionArg] = None, **kwargs: Unpack[OptionalCaptureArgs]) -> Optional[str]
+function posthog.client.add_context_tags(properties)
+function posthog.client.get_identity_state(passed) -> tuple[str, bool]
+function posthog.client.no_throw(default_return=None)
+function posthog.client.stringify_id(val)
+function posthog.contexts.get_capture_exception_code_variables_context() -> Optional[bool]
+function posthog.contexts.get_code_variables_ignore_patterns_context() -> Optional[list]
+function posthog.contexts.get_code_variables_mask_patterns_context() -> Optional[list]
+function posthog.contexts.get_context_device_id() -> Optional[str]
+function posthog.contexts.get_context_distinct_id() -> Optional[str]
+function posthog.contexts.get_context_session_id() -> Optional[str]
+function posthog.contexts.get_tags() -> Dict[str, Any]
+function posthog.contexts.identify_context(distinct_id: str) -> None
+function posthog.contexts.new_context(fresh: bool = False, capture_exceptions: bool = True, client: Optional[Client] = None)
+function posthog.contexts.scoped(fresh: bool = False, capture_exceptions: bool = True)
+function posthog.contexts.set_capture_exception_code_variables_context(enabled: bool) -> None
+function posthog.contexts.set_code_variables_ignore_patterns_context(ignore_patterns: list) -> None
+function posthog.contexts.set_code_variables_mask_patterns_context(mask_patterns: list) -> None
+function posthog.contexts.set_context_device_id(device_id: str) -> None
+function posthog.contexts.set_context_session(session_id: str) -> None
+function posthog.contexts.tag(key: str, value: Any) -> None
+function posthog.evaluate_flags(distinct_id=None, groups=None, person_properties=None, group_properties=None, only_evaluate_locally=False, disable_geoip=None, flag_keys=None, device_id=None) -> FeatureFlagEvaluations
+function posthog.exception_utils.attach_code_variables_to_frames(all_exceptions, exc_info, mask_patterns, ignore_patterns)
+function posthog.exception_utils.construct_artificial_traceback(e)
+function posthog.exception_utils.event_hint_with_exc_info(exc_info=None)
+function posthog.exception_utils.exc_info_from_error(error)
+function posthog.exception_utils.exception_is_already_captured(error)
+function posthog.exception_utils.exceptions_from_error(exc_type, exc_value, tb, mechanism=None, exception_id=0, parent_id=0, source=None)
+function posthog.exception_utils.exceptions_from_error_tuple(exc_info, mechanism=None)
+function posthog.exception_utils.filename_for_module(module, abs_path)
+function posthog.exception_utils.format_timestamp(value)
+function posthog.exception_utils.get_errno(exc_value)
+function posthog.exception_utils.get_error_message(exc_value)
+function posthog.exception_utils.get_lines_from_file(filename, lineno, max_length=None, loader=None, module=None)
+function posthog.exception_utils.get_source_context(frame, tb_lineno, max_value_length=None)
+function posthog.exception_utils.get_type_module(cls)
+function posthog.exception_utils.get_type_name(cls)
+function posthog.exception_utils.handle_in_app(event, in_app_exclude=None, in_app_include=None, project_root=None)
+function posthog.exception_utils.iter_event_frames(event)
+function posthog.exception_utils.iter_event_stacktraces(event)
+function posthog.exception_utils.iter_stacks(tb)
+function posthog.exception_utils.mark_exception_as_captured(error, uuid)
+function posthog.exception_utils.safe_repr(value)
+function posthog.exception_utils.safe_str(value)
+function posthog.exception_utils.serialize_code_variables(frame, limiter, mask_patterns=None, ignore_patterns=None, max_length=1024)
+function posthog.exception_utils.serialize_frame(frame, tb_lineno=None, max_value_length=None)
+function posthog.exception_utils.set_in_app_in_frames(frames, in_app_exclude, in_app_include, project_root=None)
+function posthog.exception_utils.should_hide_frame(frame: FrameType) -> bool
+function posthog.exception_utils.single_exception_from_error_tuple(exc_type, exc_value, tb, mechanism=None, exception_id=None, parent_id=None, source=None)
+function posthog.exception_utils.strip_string(value, max_length=None)
+function posthog.exception_utils.to_string(value)
+function posthog.exception_utils.to_timestamp(value)
+function posthog.exception_utils.try_attach_code_variables_to_frames(all_exceptions, exc_info, mask_patterns, ignore_patterns)
+function posthog.exception_utils.walk_exception_chain(exc_info)
+function posthog.feature_enabled(key, distinct_id, groups=None, person_properties=None, group_properties=None, only_evaluate_locally=False, send_feature_flag_events=True, disable_geoip=None, device_id=None)
+function posthog.feature_flag_definitions()
+function posthog.feature_flags.evaluate_flag_dependency(property, flags_by_key, evaluation_cache, distinct_id, properties, cohort_properties, device_id=None)
+function posthog.feature_flags.get_matching_variant(flag, bucketing_value)
+function posthog.feature_flags.is_condition_match(feature_flag, distinct_id, condition, properties, cohort_properties, flags_by_key=None, evaluation_cache=None, *, bucketing_value, device_id=None) -> ConditionMatch
+function posthog.feature_flags.match_cohort(property, property_values, cohort_properties, flags_by_key=None, evaluation_cache=None, distinct_id=None, device_id=None) -> bool
+function posthog.feature_flags.match_feature_flag_properties(flag, distinct_id, properties, *, cohort_properties=None, flags_by_key=None, evaluation_cache=None, device_id=None, bucketing_value=None, group_type_mapping=None, groups=None, group_properties=None) -> FlagValue
+function posthog.feature_flags.match_property(property, property_values) -> bool
+function posthog.feature_flags.match_property_group(property_group, property_values, cohort_properties, flags_by_key=None, evaluation_cache=None, distinct_id=None, device_id=None) -> bool
+function posthog.feature_flags.matches_dependency_value(expected_value, actual_value)
+function posthog.feature_flags.parse_datetime(value: str) -> datetime.datetime
+function posthog.feature_flags.parse_semver(value: str) -> tuple
+function posthog.feature_flags.relative_date_parse_for_feature_flag_matching(value: str) -> Optional[datetime.datetime]
+function posthog.feature_flags.resolve_bucketing_value(flag, distinct_id, device_id=None)
+function posthog.feature_flags.variant_lookup_table(feature_flag)
+function posthog.flush() -> None
+function posthog.get_all_flags(distinct_id, groups=None, person_properties=None, group_properties=None, only_evaluate_locally=False, disable_geoip=None, device_id=None, flag_keys_to_evaluate=None) -> Optional[dict[str, FeatureFlag]]
+function posthog.get_all_flags_and_payloads(distinct_id, groups=None, person_properties=None, group_properties=None, only_evaluate_locally=False, disable_geoip=None, device_id=None, flag_keys_to_evaluate=None) -> FlagsAndPayloads
+function posthog.get_feature_flag(key, distinct_id, groups=None, person_properties=None, group_properties=None, only_evaluate_locally=False, send_feature_flag_events=True, disable_geoip=None, device_id=None) -> Optional[FeatureFlag]
+function posthog.get_feature_flag_payload(key, distinct_id, match_value=None, groups=None, person_properties=None, group_properties=None, only_evaluate_locally=False, send_feature_flag_events=True, disable_geoip=None, device_id=None) -> Optional[str]
+function posthog.get_feature_flag_result(key, distinct_id, groups=None, person_properties=None, group_properties=None, only_evaluate_locally=False, send_feature_flag_events=True, disable_geoip=None, device_id=None)
+function posthog.get_remote_config_payload(key)
+function posthog.get_tags() -> Dict[str, Any]
+function posthog.group_identify(group_type, group_key, properties=None, timestamp=None, uuid=None, disable_geoip=None, distinct_id=None)
+function posthog.identify_context(distinct_id: str)
+function posthog.integrations.django.markcoroutinefunction(func)
+function posthog.join() -> None
+function posthog.load_feature_flags()
+function posthog.new_context(fresh: bool = False, capture_exceptions: bool = True, client: Optional[Client] = None)
+function posthog.request.batch_post(api_key: str, host: Optional[str] = None, gzip: bool = False, timeout: int = 15, path: str = EVENTS_ENDPOINT, **kwargs) -> requests.Response
+function posthog.request.determine_server_host(host: Optional[str]) -> str
+function posthog.request.disable_connection_reuse() -> None
+function posthog.request.enable_keep_alive() -> None
+function posthog.request.flags(api_key: str, host: Optional[str] = None, gzip: bool = False, timeout: int = 15, **kwargs) -> Any
+function posthog.request.get(api_key: str, url: str, host: Optional[str] = None, timeout: Optional[int] = None, etag: Optional[str] = None) -> GetResponse
+function posthog.request.is_ai_event(event_name) -> bool
+function posthog.request.normalize_host(host: Optional[str]) -> str
+function posthog.request.post(api_key: str, host: Optional[str] = None, path: Optional[str] = None, gzip: bool = False, timeout: int = 15, session: Optional[requests.Session] = None, **kwargs) -> requests.Response
+function posthog.request.remote_config(personal_api_key: str, project_api_key: str, host: Optional[str] = None, key: str = '', timeout: int = 15) -> Any
+function posthog.request.reset_sessions() -> None
+function posthog.request.set_socket_options(socket_options: Optional[SocketOptions]) -> None
+function posthog.scoped(fresh=False, capture_exceptions=True)
+function posthog.set(**kwargs: Unpack[OptionalSetArgs]) -> Optional[str]
+function posthog.set_capture_exception_code_variables_context(enabled: bool)
+function posthog.set_code_variables_ignore_patterns_context(ignore_patterns: list)
+function posthog.set_code_variables_mask_patterns_context(mask_patterns: list)
+function posthog.set_context_device_id(device_id: str)
+function posthog.set_context_session(session_id: str)
+function posthog.set_once(**kwargs: Unpack[OptionalSetArgs]) -> Optional[str]
+function posthog.setup() -> Client
+function posthog.shutdown() -> None
+function posthog.tag(name: str, value: Any)
+function posthog.types.normalize_flags_response(resp: Any) -> FlagsResponse
+function posthog.types.to_flags_and_payloads(resp: FlagsResponse) -> FlagsAndPayloads
+function posthog.types.to_payloads(response: FlagsResponse) -> Optional[dict[str, str]]
+function posthog.types.to_values(response: FlagsResponse) -> Optional[dict[str, FlagValue]]
+function posthog.utils.clean(item)
+function posthog.utils.convert_to_datetime_aware(date_obj)
+function posthog.utils.get_os_info()
+function posthog.utils.guess_timezone(dt: datetime) -> datetime
+function posthog.utils.is_naive(dt: datetime) -> bool
+function posthog.utils.is_valid_regex(value) -> bool
+function posthog.utils.remove_trailing_slash(host: str) -> str
+function posthog.utils.str_icontains(source, search)
+function posthog.utils.str_iequals(value, comparand)
+function posthog.utils.system_context() -> dict[str, Any]
+function posthog.utils.total_seconds(delta: timedelta) -> float
+method posthog.ai.anthropic.anthropic.WrappedMessages.create(posthog_distinct_id: Optional[str] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: bool = False, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs: Any)
+method posthog.ai.anthropic.anthropic.WrappedMessages.stream(posthog_distinct_id: Optional[str] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: bool = False, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs: Any)
+method posthog.ai.anthropic.anthropic_async.AsyncWrappedMessages.create(posthog_distinct_id: Optional[str] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: bool = False, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs: Any)
+method posthog.ai.anthropic.anthropic_async.AsyncWrappedMessages.stream(posthog_distinct_id: Optional[str] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: bool = False, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs: Any)
+method posthog.ai.claude_agent_sdk.client.PostHogClaudeSDKClient.connect(prompt: Any = None) -> None
+method posthog.ai.claude_agent_sdk.client.PostHogClaudeSDKClient.disconnect() -> None
+method posthog.ai.claude_agent_sdk.client.PostHogClaudeSDKClient.interrupt() -> None
+method posthog.ai.claude_agent_sdk.client.PostHogClaudeSDKClient.query(prompt: str, session_id: str = 'default') -> None
+method posthog.ai.claude_agent_sdk.client.PostHogClaudeSDKClient.receive_response()
+method posthog.ai.claude_agent_sdk.client.PostHogClaudeSDKClient.set_model(model: Optional[str] = None) -> None
+method posthog.ai.claude_agent_sdk.client.PostHogClaudeSDKClient.set_permission_mode(mode: str) -> None
+method posthog.ai.claude_agent_sdk.processor.PostHogClaudeAgentProcessor.query(*, prompt: Any, options: Optional[ClaudeAgentOptions] = None, transport: Any = None, posthog_distinct_id: Optional[Union[str, Callable[[ResultMessage], Optional[str]]]] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: Optional[bool] = None, posthog_groups: Optional[Dict[str, Any]] = None)
+method posthog.ai.gemini.gemini.Models.embed_content(model: str, contents, posthog_distinct_id: Optional[str] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: Optional[bool] = None, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs: Any)
+method posthog.ai.gemini.gemini.Models.generate_content(model: str, contents, posthog_distinct_id: Optional[str] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: Optional[bool] = None, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs: Any)
+method posthog.ai.gemini.gemini.Models.generate_content_stream(model: str, contents, posthog_distinct_id: Optional[str] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: Optional[bool] = None, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs: Any)
+method posthog.ai.gemini.gemini_async.AsyncModels.embed_content(model: str, contents, posthog_distinct_id: Optional[str] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: Optional[bool] = None, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs: Any)
+method posthog.ai.gemini.gemini_async.AsyncModels.generate_content(model: str, contents, posthog_distinct_id: Optional[str] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: Optional[bool] = None, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs: Any)
+method posthog.ai.gemini.gemini_async.AsyncModels.generate_content_stream(model: str, contents, posthog_distinct_id: Optional[str] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: Optional[bool] = None, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs: Any)
+method posthog.ai.langchain.callbacks.CallbackHandler.on_agent_action(action: AgentAction, *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any) -> Any
+method posthog.ai.langchain.callbacks.CallbackHandler.on_agent_finish(finish: AgentFinish, *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any) -> Any
+method posthog.ai.langchain.callbacks.CallbackHandler.on_chain_end(outputs: Dict[str, Any], *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any)
+method posthog.ai.langchain.callbacks.CallbackHandler.on_chain_error(error: BaseException, *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any)
+method posthog.ai.langchain.callbacks.CallbackHandler.on_chain_start(serialized: Dict[str, Any], inputs: Dict[str, Any], *, run_id: UUID, parent_run_id: Optional[UUID] = None, metadata: Optional[Dict[str, Any]] = None, **kwargs)
+method posthog.ai.langchain.callbacks.CallbackHandler.on_chat_model_start(serialized: Dict[str, Any], messages: List[List[BaseMessage]], *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs)
+method posthog.ai.langchain.callbacks.CallbackHandler.on_llm_end(response: LLMResult, *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any)
+method posthog.ai.langchain.callbacks.CallbackHandler.on_llm_error(error: BaseException, *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any)
+method posthog.ai.langchain.callbacks.CallbackHandler.on_llm_new_token(token: str, *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any) -> Any
+method posthog.ai.langchain.callbacks.CallbackHandler.on_llm_start(serialized: Dict[str, Any], prompts: List[str], *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any)
+method posthog.ai.langchain.callbacks.CallbackHandler.on_retriever_end(documents: Sequence[Document], *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any)
+method posthog.ai.langchain.callbacks.CallbackHandler.on_retriever_error(error: BaseException, *, run_id: UUID, parent_run_id: Optional[UUID] = None, tags: Optional[list[str]] = None, **kwargs: Any) -> Any
+method posthog.ai.langchain.callbacks.CallbackHandler.on_retriever_start(serialized: Optional[Dict[str, Any]], query: str, *, run_id: UUID, parent_run_id: Optional[UUID] = None, metadata: Optional[Dict[str, Any]] = None, **kwargs: Any) -> Any
+method posthog.ai.langchain.callbacks.CallbackHandler.on_tool_end(output: str, *, run_id: UUID, parent_run_id: Optional[UUID] = None, **kwargs: Any) -> Any
+method posthog.ai.langchain.callbacks.CallbackHandler.on_tool_error(error: BaseException, *, run_id: UUID, parent_run_id: Optional[UUID] = None, tags: Optional[list[str]] = None, **kwargs: Any) -> Any
+method posthog.ai.langchain.callbacks.CallbackHandler.on_tool_start(serialized: Optional[Dict[str, Any]], input_str: str, *, run_id: UUID, parent_run_id: Optional[UUID] = None, metadata: Optional[Dict[str, Any]] = None, **kwargs: Any) -> Any
+method posthog.ai.openai.openai.WrappedBetaCompletions.parse(posthog_distinct_id: Optional[str] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: bool = False, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs: Any)
+method posthog.ai.openai.openai.WrappedCompletions.create(posthog_distinct_id: Optional[str] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: bool = False, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs: Any)
+method posthog.ai.openai.openai.WrappedCompletions.parse(posthog_distinct_id: Optional[str] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: bool = False, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs: Any)
+method posthog.ai.openai.openai.WrappedEmbeddings.create(posthog_distinct_id: Optional[str] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: bool = False, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs: Any)
+method posthog.ai.openai.openai.WrappedResponses.create(posthog_distinct_id: Optional[str] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: bool = False, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs: Any)
+method posthog.ai.openai.openai.WrappedResponses.parse(posthog_distinct_id: Optional[str] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: bool = False, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs: Any)
+method posthog.ai.openai.openai_async.WrappedBetaCompletions.parse(posthog_distinct_id: Optional[str] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: bool = False, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs: Any)
+method posthog.ai.openai.openai_async.WrappedCompletions.create(posthog_distinct_id: Optional[str] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: bool = False, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs: Any)
+method posthog.ai.openai.openai_async.WrappedCompletions.parse(posthog_distinct_id: Optional[str] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: bool = False, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs: Any)
+method posthog.ai.openai.openai_async.WrappedEmbeddings.create(posthog_distinct_id: Optional[str] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: bool = False, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs: Any)
+method posthog.ai.openai.openai_async.WrappedResponses.create(posthog_distinct_id: Optional[str] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: bool = False, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs: Any)
+method posthog.ai.openai.openai_async.WrappedResponses.parse(posthog_distinct_id: Optional[str] = None, posthog_trace_id: Optional[str] = None, posthog_properties: Optional[Dict[str, Any]] = None, posthog_privacy_mode: bool = False, posthog_groups: Optional[Dict[str, Any]] = None, **kwargs: Any)
+method posthog.ai.openai_agents.processor.PostHogTracingProcessor.force_flush() -> None
+method posthog.ai.openai_agents.processor.PostHogTracingProcessor.on_span_end(span: Span[Any]) -> None
+method posthog.ai.openai_agents.processor.PostHogTracingProcessor.on_span_start(span: Span[Any]) -> None
+method posthog.ai.openai_agents.processor.PostHogTracingProcessor.on_trace_end(trace: Trace) -> None
+method posthog.ai.openai_agents.processor.PostHogTracingProcessor.on_trace_start(trace: Trace) -> None
+method posthog.ai.openai_agents.processor.PostHogTracingProcessor.shutdown() -> None
+method posthog.ai.otel.exporter.PostHogTraceExporter.export(spans: Sequence[ReadableSpan]) -> SpanExportResult
+method posthog.ai.otel.exporter.PostHogTraceExporter.force_flush(timeout_millis: Optional[int] = None) -> bool
+method posthog.ai.otel.exporter.PostHogTraceExporter.shutdown() -> None
+method posthog.ai.otel.processor.PostHogSpanProcessor.force_flush(timeout_millis: Optional[int] = None) -> bool
+method posthog.ai.otel.processor.PostHogSpanProcessor.on_end(span: ReadableSpan) -> None
+method posthog.ai.otel.processor.PostHogSpanProcessor.on_start(span: Span, parent_context: Optional[Context] = None) -> None
+method posthog.ai.otel.processor.PostHogSpanProcessor.shutdown() -> None
+method posthog.ai.prompts.Prompts.clear_cache(name: Optional[str] = None, *, version: Optional[int] = None) -> None
+method posthog.ai.prompts.Prompts.compile(prompt: str, variables: PromptVariables) -> str
+method posthog.ai.prompts.Prompts.get(name: str, *, with_metadata: Optional[bool] = None, cache_ttl_seconds: Optional[int] = None, fallback: Optional[str] = None, version: Optional[int] = None) -> Union[str, PromptResult]
+method posthog.bucketed_rate_limiter.BucketedRateLimiter.consume_rate_limit(key: Hashable) -> bool
+method posthog.bucketed_rate_limiter.BucketedRateLimiter.stop() -> None
+method posthog.client.Client.alias(previous_id: str, distinct_id: Optional[str], timestamp=None, uuid=None, disable_geoip=None)
+method posthog.client.Client.capture(event: str, **kwargs: Unpack[OptionalCaptureArgs]) -> Optional[str]
+method posthog.client.Client.capture_exception(exception: Optional[ExceptionArg], **kwargs: Unpack[OptionalCaptureArgs]) -> Optional[str]
+method posthog.client.Client.evaluate_flags(distinct_id: Optional[ID_TYPES] = None, *, groups: Optional[Dict[str, str]] = None, person_properties: Optional[Dict[str, Any]] = None, group_properties: Optional[Dict[str, Dict[str, Any]]] = None, only_evaluate_locally: bool = False, disable_geoip: Optional[bool] = None, flag_keys: Optional[List[str]] = None, device_id: Optional[str] = None) -> FeatureFlagEvaluations
+method posthog.client.Client.feature_enabled(key, distinct_id, *, groups=None, person_properties=None, group_properties=None, only_evaluate_locally=False, send_feature_flag_events=True, disable_geoip=None, device_id: Optional[str] = None)
+method posthog.client.Client.feature_flag_definitions()
+method posthog.client.Client.flush() -> None
+method posthog.client.Client.get_all_flags(distinct_id, *, groups=None, person_properties=None, group_properties=None, only_evaluate_locally=False, disable_geoip=None, flag_keys_to_evaluate: Optional[list[str]] = None, device_id: Optional[str] = None) -> Optional[dict[str, Union[bool, str]]]
+method posthog.client.Client.get_all_flags_and_payloads(distinct_id, *, groups=None, person_properties=None, group_properties=None, only_evaluate_locally=False, disable_geoip=None, flag_keys_to_evaluate: Optional[list[str]] = None, device_id: Optional[str] = None) -> FlagsAndPayloads
+method posthog.client.Client.get_feature_flag(key, distinct_id, *, groups=None, person_properties=None, group_properties=None, only_evaluate_locally=False, send_feature_flag_events=True, disable_geoip=None, device_id: Optional[str] = None) -> Optional[FlagValue]
+method posthog.client.Client.get_feature_flag_payload(key, distinct_id, *, match_value: Optional[FlagValue] = None, groups=None, person_properties=None, group_properties=None, only_evaluate_locally=False, send_feature_flag_events=False, disable_geoip=None, device_id: Optional[str] = None)
+method posthog.client.Client.get_feature_flag_result(key, distinct_id, *, groups=None, person_properties=None, group_properties=None, only_evaluate_locally=False, send_feature_flag_events=True, disable_geoip=None, device_id: Optional[str] = None) -> Optional[FeatureFlagResult]
+method posthog.client.Client.get_feature_flags_and_payloads(distinct_id, groups=None, person_properties=None, group_properties=None, disable_geoip=None, flag_keys_to_evaluate: Optional[list[str]] = None, device_id: Optional[str] = None) -> FlagsAndPayloads
+method posthog.client.Client.get_feature_payloads(distinct_id, groups=None, person_properties=None, group_properties=None, disable_geoip=None, flag_keys_to_evaluate: Optional[list[str]] = None, device_id: Optional[str] = None) -> dict[str, str]
+method posthog.client.Client.get_feature_variants(distinct_id, groups=None, person_properties=None, group_properties=None, disable_geoip=None, flag_keys_to_evaluate: Optional[list[str]] = None, device_id: Optional[str] = None) -> dict[str, Union[bool, str]]
+method posthog.client.Client.get_flags_decision(distinct_id: Optional[ID_TYPES] = None, groups: Optional[dict] = None, person_properties=None, group_properties=None, disable_geoip=None, flag_keys_to_evaluate: Optional[list[str]] = None, device_id: Optional[str] = None) -> FlagsResponse
+method posthog.client.Client.get_remote_config_payload(key: str)
+method posthog.client.Client.group_identify(group_type: str, group_key: str, properties: Optional[Dict[str, Any]] = None, timestamp: Optional[Union[datetime, str]] = None, uuid: Optional[str] = None, disable_geoip: Optional[bool] = None, distinct_id: Optional[ID_TYPES] = None) -> Optional[str]
+method posthog.client.Client.join() -> None
+method posthog.client.Client.load_feature_flags()
+method posthog.client.Client.new_context(fresh=False, capture_exceptions=True)
+method posthog.client.Client.set(**kwargs: Unpack[OptionalSetArgs]) -> Optional[str]
+method posthog.client.Client.set_once(**kwargs: Unpack[OptionalSetArgs]) -> Optional[str]
+method posthog.client.Client.shutdown() -> None
+method posthog.consumer.Consumer.next()
+method posthog.consumer.Consumer.pause()
+method posthog.consumer.Consumer.request(batch)
+method posthog.consumer.Consumer.run()
+method posthog.consumer.Consumer.upload()
+method posthog.contexts.ContextScope.add_tag(key: str, value: Any)
+method posthog.contexts.ContextScope.collect_tags() -> Dict[str, Any]
+method posthog.contexts.ContextScope.get_capture_exception_code_variables() -> Optional[bool]
+method posthog.contexts.ContextScope.get_code_variables_ignore_patterns() -> Optional[list]
+method posthog.contexts.ContextScope.get_code_variables_mask_patterns() -> Optional[list]
+method posthog.contexts.ContextScope.get_device_id() -> Optional[str]
+method posthog.contexts.ContextScope.get_distinct_id() -> Optional[str]
+method posthog.contexts.ContextScope.get_parent()
+method posthog.contexts.ContextScope.get_session_id() -> Optional[str]
+method posthog.contexts.ContextScope.set_capture_exception_code_variables(enabled: bool)
+method posthog.contexts.ContextScope.set_code_variables_ignore_patterns(ignore_patterns: list)
+method posthog.contexts.ContextScope.set_code_variables_mask_patterns(mask_patterns: list)
+method posthog.contexts.ContextScope.set_device_id(device_id: str)
+method posthog.contexts.ContextScope.set_distinct_id(distinct_id: str)
+method posthog.contexts.ContextScope.set_session_id(session_id: str)
+method posthog.exception_capture.ExceptionCapture.capture_exception(exception, metadata=None)
+method posthog.exception_capture.ExceptionCapture.close()
+method posthog.exception_capture.ExceptionCapture.exception_handler(exc_type, exc_value, exc_traceback)
+method posthog.exception_capture.ExceptionCapture.exception_receiver(exc_info, extra_properties)
+method posthog.exception_capture.ExceptionCapture.thread_exception_handler(args)
+method posthog.exception_utils.AnnotatedValue.removed_because_over_size_limit()
+method posthog.exception_utils.AnnotatedValue.removed_because_raw_data()
+method posthog.exception_utils.AnnotatedValue.substituted_because_contains_sensitive_data()
+method posthog.exception_utils.VariableSizeLimiter.add(size)
+method posthog.exception_utils.VariableSizeLimiter.can_add(size)
+method posthog.exception_utils.VariableSizeLimiter.get_remaining_space()
+method posthog.feature_flag_evaluations.FeatureFlagEvaluations.get_flag(key: str) -> Optional[FlagValue]
+method posthog.feature_flag_evaluations.FeatureFlagEvaluations.get_flag_payload(key: str) -> Optional[Any]
+method posthog.feature_flag_evaluations.FeatureFlagEvaluations.is_enabled(key: str) -> bool
+method posthog.feature_flag_evaluations.FeatureFlagEvaluations.only(keys: List[str]) -> FeatureFlagEvaluations
+method posthog.feature_flag_evaluations.FeatureFlagEvaluations.only_accessed() -> FeatureFlagEvaluations
+method posthog.flag_definition_cache.FlagDefinitionCacheProvider.get_flag_definitions() -> Union[Optional[FlagDefinitionCacheData], Awaitable[Optional[FlagDefinitionCacheData]]]
+method posthog.flag_definition_cache.FlagDefinitionCacheProvider.on_flag_definitions_received(data: FlagDefinitionCacheData) -> Optional[Awaitable[None]]
+method posthog.flag_definition_cache.FlagDefinitionCacheProvider.should_fetch_flag_definitions() -> Union[bool, Awaitable[bool]]
+method posthog.flag_definition_cache.FlagDefinitionCacheProvider.shutdown() -> Optional[Awaitable[None]]
+method posthog.integrations.celery.PosthogCeleryIntegration.instrument() -> None
+method posthog.integrations.celery.PosthogCeleryIntegration.shutdown() -> None
+method posthog.integrations.celery.PosthogCeleryIntegration.uninstrument() -> None
+method posthog.integrations.django.PosthogContextMiddleware.aextract_request_user(request)
+method posthog.integrations.django.PosthogContextMiddleware.aextract_tags(request)
+method posthog.integrations.django.PosthogContextMiddleware.extract_request_user(request)
+method posthog.integrations.django.PosthogContextMiddleware.extract_tags(request)
+method posthog.integrations.django.PosthogContextMiddleware.process_exception(request, exception)
+method posthog.poller.Poller.run()
+method posthog.poller.Poller.stop()
+method posthog.request.DatetimeSerializer.default(obj: Any)
+method posthog.request.HTTPAdapterWithSocketOptions.init_poolmanager(*args, **kwargs)
+method posthog.types.FeatureFlag.from_json(resp: Any) -> FeatureFlag
+method posthog.types.FeatureFlag.from_value_and_payload(key: str, value: FlagValue, payload: Any) -> FeatureFlag
+method posthog.types.FeatureFlag.get_value() -> FlagValue
+method posthog.types.FeatureFlagError.api_error(status: Union[int, str]) -> str
+method posthog.types.FeatureFlagResult.from_flag_details(details: Union[FeatureFlag, None], override_match_value: Optional[FlagValue] = None) -> FeatureFlagResult | None
+method posthog.types.FeatureFlagResult.from_value_and_payload(key: str, value: Union[FlagValue, None], payload: Any) -> Union[FeatureFlagResult, None]
+method posthog.types.FeatureFlagResult.get_value() -> FlagValue
+method posthog.types.FlagMetadata.from_json(resp: Any) -> Union[FlagMetadata, LegacyFlagMetadata]
+method posthog.types.FlagReason.from_json(resp: Any) -> Optional[FlagReason]
+method posthog.utils.FlagCache.clear()
+method posthog.utils.FlagCache.get_cached_flag(distinct_id, flag_key, current_flag_version)
+method posthog.utils.FlagCache.get_stale_cached_flag(distinct_id, flag_key, max_stale_age=None)
+method posthog.utils.FlagCache.invalidate_version(old_version)
+method posthog.utils.FlagCache.set_cached_flag(distinct_id, flag_key, flag_result, flag_definition_version)
+method posthog.utils.FlagCacheEntry.is_stale_but_usable(current_time, max_stale_age=CACHE_STALE_TTL)
+method posthog.utils.FlagCacheEntry.is_valid(current_time, ttl, current_flag_version)
+method posthog.utils.RedisFlagCache.clear()
+method posthog.utils.RedisFlagCache.get_cached_flag(distinct_id, flag_key, current_flag_version)
+method posthog.utils.RedisFlagCache.get_stale_cached_flag(distinct_id, flag_key, max_stale_age=None)
+method posthog.utils.RedisFlagCache.invalidate_version(old_version)
+method posthog.utils.RedisFlagCache.set_cached_flag(distinct_id, flag_key, flag_result, flag_definition_version)
+module posthog
+module posthog.ai
+module posthog.ai.anthropic
+module posthog.ai.anthropic.anthropic
+module posthog.ai.anthropic.anthropic_async
+module posthog.ai.anthropic.anthropic_converter
+module posthog.ai.anthropic.anthropic_providers
+module posthog.ai.claude_agent_sdk
+module posthog.ai.claude_agent_sdk.client
+module posthog.ai.claude_agent_sdk.processor
+module posthog.ai.gateway
+module posthog.ai.gemini
+module posthog.ai.gemini.gemini
+module posthog.ai.gemini.gemini_async
+module posthog.ai.gemini.gemini_converter
+module posthog.ai.langchain
+module posthog.ai.langchain.callbacks
+module posthog.ai.openai
+module posthog.ai.openai.openai
+module posthog.ai.openai.openai_async
+module posthog.ai.openai.openai_converter
+module posthog.ai.openai.openai_providers
+module posthog.ai.openai.wrapper_utils
+module posthog.ai.openai_agents
+module posthog.ai.openai_agents.processor
+module posthog.ai.otel
+module posthog.ai.otel.exporter
+module posthog.ai.otel.processor
+module posthog.ai.otel.spans
+module posthog.ai.prompts
+module posthog.ai.sanitization
+module posthog.ai.stream
+module posthog.ai.types
+module posthog.ai.utils
+module posthog.args
+module posthog.bucketed_rate_limiter
+module posthog.client
+module posthog.consumer
+module posthog.contexts
+module posthog.exception_capture
+module posthog.exception_utils
+module posthog.feature_flag_evaluations
+module posthog.feature_flags
+module posthog.flag_definition_cache
+module posthog.integrations
+module posthog.integrations.celery
+module posthog.integrations.django
+module posthog.poller
+module posthog.request
+module posthog.types
+module posthog.utils
+module posthog.version
diff --git a/uv.lock b/uv.lock
index 1a7598c5..bc294109 100644
--- a/uv.lock
+++ b/uv.lock
@@ -9,7 +9,7 @@ resolution-markers = [
]
[options]
-exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values.
+exclude-newer = "2026-06-05T14:10:56.136305Z"
exclude-newer-span = "P7D"
[[package]]
@@ -827,6 +827,41 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/5c/4f/aab73ecaa6b3086a4c89863d94cf26fa84cbff63f52ce9bc4342b3087a06/greenlet-3.2.3-cp314-cp314-win_amd64.whl", hash = "sha256:8c47aae8fbbfcf82cc13327ae802ba13c9c36753b67e760023fd116bc124a62a", size = 301236, upload-time = "2025-06-05T16:15:20.111Z" },
]
+[[package]]
+name = "griffe"
+version = "2.0.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "griffecli" },
+ { name = "griffelib" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/4a/49/eb6d2935e27883af92c930ed40cc4c69bcd32c402be43b8ca4ab20510f67/griffe-2.0.2.tar.gz", hash = "sha256:c5d56326d159f274492e9bf93a9895cec101155d944caa66d0fc4e0c13751b92", size = 293757, upload-time = "2026-03-27T11:34:52.205Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/94/c0/2bb018eecf9a83c68db9cd9fffd9dab25f102ad30ed869451046e46d1187/griffe-2.0.2-py3-none-any.whl", hash = "sha256:2b31816460aee1996af26050a1fc6927a2e5936486856707f55508e4c9b5960b", size = 5141, upload-time = "2026-03-27T11:34:47.721Z" },
+]
+
+[[package]]
+name = "griffecli"
+version = "2.0.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama" },
+ { name = "griffelib" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/79/e0/6a7d661d71bb043656a109b91d84a42b5342752542074ec83b16a6eb97f0/griffecli-2.0.2.tar.gz", hash = "sha256:40a1ad4181fc39685d025e119ae2c5b669acdc1f19b705fb9bf971f4e6f6dffb", size = 56281, upload-time = "2026-03-27T11:34:50.087Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2e/e8/90d93356c88ac34c20cb5edffca68138df55ca9bbd1a06eccfbcec8fdbe5/griffecli-2.0.2-py3-none-any.whl", hash = "sha256:0d44d39e59afa81e288a3e1c3bf352cc4fa537483326ac06b8bb6a51fd8303a0", size = 9500, upload-time = "2026-03-27T11:34:48.81Z" },
+]
+
+[[package]]
+name = "griffelib"
+version = "2.0.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/9d/82/74f4a3310cdabfbb10da554c3a672847f1ed33c6f61dd472681ce7f1fe67/griffelib-2.0.2.tar.gz", hash = "sha256:3cf20b3bc470e83763ffbf236e0076b1211bac1bc67de13daf494640f2de707e", size = 166461, upload-time = "2026-03-27T11:34:51.091Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/11/8c/c9138d881c79aa0ea9ed83cbd58d5ca75624378b38cee225dcf5c42cc91f/griffelib-2.0.2-py3-none-any.whl", hash = "sha256:925c857658fb1ba40c0772c37acbc2ab650bd794d9c1b9726922e36ea4117ea1", size = 142357, upload-time = "2026-03-27T11:34:46.275Z" },
+]
+
[[package]]
name = "h11"
version = "0.16.0"
@@ -2181,6 +2216,7 @@ dependencies = [
[package.optional-dependencies]
dev = [
{ name = "django-stubs" },
+ { name = "griffe" },
{ name = "lxml" },
{ name = "mypy" },
{ name = "mypy-baseline" },
@@ -2246,6 +2282,7 @@ requires-dist = [
{ name = "django-stubs", marker = "extra == 'dev'" },
{ name = "freezegun", marker = "extra == 'test'", specifier = "==1.5.1" },
{ name = "google-genai", marker = "extra == 'test'" },
+ { name = "griffe", marker = "extra == 'dev'" },
{ name = "langchain", marker = "extra == 'langchain'", specifier = ">=0.2.0" },
{ name = "langchain-anthropic", marker = "extra == 'test'", specifier = ">=1.0" },
{ name = "langchain-community", marker = "extra == 'test'", specifier = ">=0.4" },