mrkeyoor.com_
Sun 20 Sept 18:57 UTC
PyPIUtilsupdated 20 Sept 2026

posthog review

posthog 7.44.1 is PostHog's Python client for product analytics, server-side feature flags, person and group properties, exception events, and optional instrumentation around AI or MCP code. `capture()` normally hands an event to an in-memory delivery queue, while flag methods either contact the service or use definitions polled into the process. The new 7.44.0 option can copy an active OpenTelemetry trace ID and span ID onto captured events; it is off by default and explicit event properties win. Patch 7.44.1 also makes local `is_set` and `is_not_set` flag rules behave correctly when the caller supplies only part of the property context. Our measured 7.42.0 import took 0.93 seconds.

Verdict

posthog 7.42.0 installed in 0.3 seconds and used 6 MB across 9 packages in our sandbox, with 0 audit findings and a working 0.93-second import. The current 7.44.1 client belongs in services already committed to PostHog, provided every process has an explicit queue-drain path.

We installed it

Lab card: what happened when we installed posthogScreenshot of posthog documentation
Install✓ · 0.3s9 packages on disk · 6 MB
Importimport posthog in 0.93s · pure Python · py.typed · requires Python >=3.10
Known vulns0(pip-audit)

Answers from our run

Does posthog install cleanly?

Yes. In a fresh container with an empty cache, pip install posthog finished in 0.3s, leaving 9 packages and 6 MB on disk. pip-audit reported no known vulnerabilities.

What does posthog need to run?

Python >=3.10, and nothing compiled: it is pure Python. In our run import posthog succeeded in 0.93s, and the package ships py.typed for type checkers.

posthog or sentry-sdk: which should you use?

sentry-sdk: Use it when exception grouping, tracing, releases, and operational alerts matter more than product funnels. posthog 7.42.0 installed in 0.3 seconds and used 6 MB across 9 packages in our sandbox, with 0 audit findings and a working 0.93-second import.

When should you not use posthog?

Your analytics destination is undecided or must remain portable. Event capture, feature flags, and identity semantics in this package are specific to PostHog.

API stability3/5Capture, identity, groups, and feature flags remain the recognizable core, yet behavioral details change often inside 7.x. Version 7.43.0 added remote fallback when a key is absent from local definitions, 7.43.1 changed explicitly empty flag snapshots, 7.44.0 introduced trace-context capture, and 7.44.1 corrected partial-property local rules. Those changes can alter latency or a flag result without changing the caller's method name.
Docs4/5PostHog publishes dedicated pages for the Python library, Django, Flask, feature flags, OpenFeature, AI instrumentation, and other product areas. The package changelog states concrete behavior changes, dates, and the commits that introduced them. The repository README mostly redirects readers elsewhere, so queue lifecycle, regional hosts, server-side secrets, local evaluation, and integration-specific privacy settings must be assembled from several pages.
Maintenance5/5Version 7.44.1 was released on August 26, 2026, the repository was pushed the same day, and GitHub reports 7 open issues and pull requests. Releases immediately before it addressed local-to-remote flag fallback, empty snapshots, trace correlation, MCP session warnings, and error redaction. This is active vendor maintenance, though the pace makes an exact pin and a changelog read part of each upgrade.
Ecosystem4/5PyPI reports 11,267,422 downloads in the latest week. The focused SDK repository has 61 stars, and the official documentation covers Django, Flask, Celery, OpenFeature, model-provider wrappers, MCP servers, exceptions, flags, and groups. Those integrations are useful inside a PostHog deployment. They do not offer backend portability because event shapes, flag evaluation, and identity behavior all target one platform.

Use it if

  • PostHog already receives browser events and payments, jobs, or webhooks need to use the same distinct IDs and event taxonomy.
  • Backend branches depend on PostHog Boolean or multivariate flags, including locally evaluated definitions.
  • Django, Flask, Celery, model-provider calls, or an MCP server should report through vendor-maintained integrations.
  • The process has a reliable shutdown point where its queued analytics events can be drained.
Skip it if

Setup reality

We installed posthog 7.42.0 in a clean Python 3.12 Bookworm sandbox. The install took 0.3 seconds, put 9 packages on disk, and used 6 MB. import posthog completed in 0.93 seconds. That pure-Python wheel requires Python 3.10 or later, ships py.typed, and declares 52 dependencies when optional requirements are counted. pip-audit found 0 known vulnerabilities, while the package metadata did not identify a license. These measurements are for 7.42.0, not current 7.44.1.

A project API key authorizes ingestion, and host must match the project's region. Local flag polling needs the more privileged server-side secret key; personal_api_key remains as a deprecated alias. Keep that credential out of browser code. Choose either one Posthog instance or module-level configuration so separate queues do not acquire different hosts and settings. Capture failures are intentionally quiet, so wire debug logs or an error callback when proving network delivery.

capture() queues events for a worker until the count or timer triggers a batch. Call flush() at a job boundary and shutdown() once during service termination. After shutdown, new captures are dropped. Forked and preloaded worker servers need a lifecycle test so threads and context belong to the child process. A before_send callback can redact or reject an event, and any exception raised by that callback causes the event to be dropped.

Local evaluation only works when the process has current definitions and the properties referenced by each rule. Since 7.43.0, a key absent from local definitions falls back to a remote request unless only_evaluate_locally=True. Version 7.44.1 corrects partial-context handling for is_set and is_not_set. Version 7.44.0 can attach OpenTelemetry trace context when capture_trace_context=True; install the otel extra and decide whether correlating IDs should leave the service before enabling it.

Patterns

Create one EU client with bounded batching configure-regional-client

import os
from posthog import Posthog

posthog = Posthog(
    os.environ['POSTHOG_PROJECT_KEY'],
    host='https://eu.i.posthog.com',
    flush_at=100,
    flush_interval=5.0,
)

Use the ingestion host shown for the project region. A request accepted by a different regional endpoint will not appear in the intended project.

Record an event from backend code capture-product-event

posthog.capture(
    'subscription upgraded',
    distinct_id='user-42',
    properties={
        'plan': 'pro',
        'source': 'billing-webhook',
    },
)

Current 7.x takes the event name first and the identity as a keyword. Reuse the frontend's stable distinct ID to keep one person history.

Set mutable and first-touch person fields write-person-properties

posthog.set(
    distinct_id='user-42',
    properties={'plan': 'pro'},
)
posthog.set_once(
    distinct_id='user-42',
    properties={'signup_source': 'organic'},
)

`set()` can replace a value later. `set_once()` preserves the first accepted value and suits acquisition fields.

Read Boolean and multivariate flags check-feature-flag

enabled = posthog.feature_enabled('new-dashboard', 'user-42')
variant = posthog.get_feature_flag(
    'pricing-test',
    'user-42',
    person_properties={'plan': 'pro'},
)

if enabled:
    show_dashboard()
if variant == 'annual-first':
    show_annual_price()

Supply every property referenced by the flag rules. Missing context can force server evaluation or select a different variant.

Evaluate several flags once per request evaluate-flag-snapshot

flags = posthog.evaluate_flags(
    'user-42',
    person_properties={'plan': 'enterprise'},
    flag_keys=['new-dashboard', 'pricing-test'],
)

if flags.is_enabled('new-dashboard', default_value=False):
    show_dashboard()
posthog.capture('dashboard viewed', distinct_id='user-42', flags=flags)

A snapshot avoids separate evaluation for each branch and can attach the values used by the request to its event.

Poll definitions and forbid network fallback force-local-flag-evaluation

posthog = Posthog(
    os.environ['POSTHOG_PROJECT_KEY'],
    secret_key=os.environ['POSTHOG_SECRET_KEY'],
    host='https://eu.i.posthog.com',
    enable_local_evaluation=True,
    poll_interval=30,
)

value = posthog.get_feature_flag(
    'new-dashboard',
    'user-42',
    person_properties={'plan': 'pro'},
    only_evaluate_locally=True,
)

Without `only_evaluate_locally=True`, 7.43.0 and later can contact the server when a key is missing from polled definitions.

Correlate events with an active OpenTelemetry span attach-trace-context

posthog = Posthog(
    os.environ['POSTHOG_PROJECT_KEY'],
    host='https://us.i.posthog.com',
    capture_trace_context=True,
)

with tracer.start_as_current_span('create-order'):
    posthog.capture('order created', distinct_id='user-42')

Version 7.44.0 adds this opt-in path. Install the `otel` extra; explicit `$trace_id` and `$span_id` event properties take precedence.

Update a company and attach it to an event associate-group

posthog.group_identify(
    group_type='company',
    group_key='acme',
    properties={'plan': 'enterprise', 'seats': 80},
)
posthog.capture(
    'report exported',
    distinct_id='user-42',
    groups={'company': 'acme'},
)

`group_identify()` updates the group profile. The `groups` argument on capture connects this event to that company.

Apply identity and tags inside one request scope-request-context

from posthog import capture, identify_context, new_context, tag

with new_context():
    identify_context('user-42')
    tag('request_id', request_id)
    tag('tenant', 'acme')
    capture('invoice generated')
    capture('email queued')

Start a new context for every request or job. Reusing one can carry a previous tenant's identity into later events.

Send a caught exception and preserve failure capture-handled-error

try:
    charge(order)
except PaymentError as error:
    posthog.capture_exception(
        error,
        distinct_id='user-42',
        properties={'order_id': order.id},
    )
    raise

Raising again keeps the application's error path intact. Analytics capture should not turn a failed payment into a successful return.

Remove secrets before an event reaches the queue redact-event-properties

def redact(event):
    properties = event.get('properties', {})
    properties.pop('authorization', None)
    if 'email' in properties:
        properties['email'] = '[redacted]'
    return event

posthog.before_send = redact

Returning `None` drops the event. An exception inside `before_send` also drops it, so test redaction code without network dependencies.

Drain a short-lived process explicitly flush-before-exit

try:
    posthog.capture(
        'nightly import finished',
        distinct_id='system',
        properties={'rows': imported_rows},
    )
finally:
    posthog.flush(timeout_seconds=5)
    posthog.shutdown()

`flush()` waits for queued delivery and keeps the client usable. `shutdown()` is terminal; later captures are discarded.

Alternatives

PackageRegistryPick it when
sentry-sdkPyPIUse it when exception grouping, tracing, releases, and operational alerts matter more than product funnels.
mixpanelPyPIUse it when Mixpanel is already the chosen product analytics and identity system.
analytics-pythonPyPIUse Segment's client when one event stream must route to several downstream tools.
opentelemetry-sdkPyPIUse it for vendor-neutral service traces and metrics rather than product analytics and experiments.

More utils guides

lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.