Migrating to PostHog Python SDK V6
Jul 29, 2025
Contents
If you're just looking for instructions to upgrade to
6.x
, skip directly to the migration guide.
Version 6.x
of the PostHog Python SDK introduces a new context()
API for scope-based state sharing across captures along with a series of breaking changes. This guide walks through changes introduced and how to migrate from 5.x
to 6.x
.
Overview
Here are the major changes from 5.x
to 6.x
:
Type | Summary |
---|---|
New | Scoped contexts for state management across event captures |
New | Django contexts middleware wraps all requests in a context, extracts user and session information from headers automatically |
Breaking | The identify() method is deprecated in favor of contexts |
Breaking | Deprecated page() and screen() methods. Prefer use of capture() on the backend or capturing page/screen view events on the client-end |
Breaking | Order of arguments updated for capture-related methods. You must update calls to use kwargs before upgrading |
Breaking | Capture methods now return the UUID of the captured event, rather than the full event object |
Breaking | Deletes unmaintained exception capture integrations, which is replaced by the general purpose Django middleware |
You should always pin versions using the compatible release clause to avoid auto-upgrading to an incompatible version accidentally.
New feature: contexts
Version 6.x
introduces contexts. Events captured under the same context scope will share properties set on the context. This can be used to identify who, where, and when an event is triggered.
This is very convenient for server-side use cases like Django apps, where each request might be handled for a different user.
Contexts can be passed down the call tree. You can iteratively add tags to the context to keep track of events leading up to a capture call. For example:
When capture
is finally called, the order_completed
event will have a custom event property called order_steps
with a list of values ["validate_order", "process_payment", "update_inventory"]
.
Contexts can also be nested. This means that:
- Parent context values are shared with child contexts.
- Child contexts can override parent context value within the child context's scope.
- When a child context exits, the overridden parent context is restored.
Learn more about contexts in the Python SDK docs.
New: Django contexts middleware
The new contexts middleware for Django wraps every request in a context. The context will automatically capture the following information:
Property | Source |
---|---|
Session ID | From X-POSTHOG-SESSION-ID header, if present |
Distinct ID | From X-POSTHOG-DISTINCT-ID header, if present |
$current_url | Current URL |
$request_method | Request method |
If you're using the PostHog JavaScript web SDK on the client-side, enabling tracing headers will populate these headers automatically.
This middleware will also automatically capture exceptions. You can learn more about this middleware in the Django integration docs.
Breaking change: identification
The identify()
method no longer exists in the Python SDK. On client SDKs, identify()
declares which user is using the device. This pattern doesn't apply to server SDKs, where each request handled could belong to a different user.
The preferred way to identify events in 6.x
is to use contexts. For example, when handling requests from the client, you can parse the distinct ID and session ID from the incoming request and set this information on a context.
All events captured under this context's scope, which is all events captured during the handling of this request, will be identified by the distinct ID of the user and session ID.
Alternatively, you can specify distinct ID explicitly during a capture and set persons properties:
Breaking change: capture method signature
Distinct ID is no longer a required first argument for capture methods like capture()
and capture_exception()
. This is a consequence of preferring contexts to identify events. When you specify a distinct ID in a identify_context()
, you won't need to pass it in again when capturing events.
Here's a before and after of what this might look like:
You can still capture events with an explicit distinct_id
like before, but it must be passed in as a keyword argument:
Capture methods also now return the event UUID instead of the full event object. You can find the exact function signature in the Python SDK reference.
Breaking change: Django error tracking integration
You might be using the old Django error tracking integration like this in 5.x
:
This is deprecated and no longer necessary in 6.x
, instead, the general purpose context middleware also handles error capturing.
Migrating from V5 to V6
If you're planning to upgrade from 5.x
to 6.x
of the Python SDK, here are the changes you need to make to complete the migration. You can do this by installing "posthog~=6.0.0"
using your package manager of choice.
1. Replace identify()
calls
The identify()
method is removed. You can instead set properties during capture or explicitly use set()
to set persons properties to create person profiles.
Before:
Now:
2. Replace screen()
and page()
calls
The methods screen()
and page()
are removed. They are aliases for the capture()
method. Instead, use capture()
explicitly to capture $pageview
or $screen
events. You can check the activity tab to see what properties are captured in a $pageview
event.
Before:
Now:
3. Update capture()
and capture_exception()
calls
The capture()
and capture_exception()
methods now take keyword arguments instead of positional arguments. The distinct ID is now optional.
Before:
Now:
4. Replace the Django error handling integration
The exception_capture
module is removed. Instead, use the context middleware to capture exceptions.
Before:
Now:
5. Adopt contexts
Contexts are new. While they're not a breaking change, we recommend that you adopt the contexts pattern for easier management of state between captured events. See the context docs to learn more.