vybekiit 0.7.34 → 0.7.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/dist/bin.js +520 -9
  2. package/dist/global-skills/eas-app-stores/SKILL.md +2 -2
  3. package/dist/global-skills/eas-hosting/SKILL.md +2 -2
  4. package/dist/global-skills/eas-observe/SKILL.md +7 -15
  5. package/dist/global-skills/eas-observe/references/queries.md +51 -10
  6. package/dist/global-skills/eas-observe/references/setup.md +60 -7
  7. package/dist/global-skills/eas-simulator/SKILL.md +2 -2
  8. package/dist/global-skills/eas-simulator/references/run-your-app.md +2 -0
  9. package/dist/global-skills/eas-update/SKILL.md +2 -2
  10. package/dist/global-skills/eas-update-insights/SKILL.md +2 -2
  11. package/dist/global-skills/eas-workflows/SKILL.md +2 -2
  12. package/dist/global-skills/expo-animation/SKILL.md +2 -2
  13. package/dist/global-skills/expo-app-clip/SKILL.md +2 -2
  14. package/dist/global-skills/expo-brownfield/SKILL.md +2 -2
  15. package/dist/global-skills/expo-data-fetching/SKILL.md +2 -2
  16. package/dist/global-skills/expo-design-system/SKILL.md +2 -2
  17. package/dist/global-skills/expo-dev-client/SKILL.md +4 -2
  18. package/dist/global-skills/expo-dom/SKILL.md +2 -2
  19. package/dist/global-skills/expo-examples/SKILL.md +2 -2
  20. package/dist/global-skills/expo-migrate-module/SKILL.md +2 -2
  21. package/dist/global-skills/expo-module/SKILL.md +2 -2
  22. package/dist/global-skills/expo-native-ui/SKILL.md +2 -2
  23. package/dist/global-skills/expo-overview/SKILL.md +9 -4
  24. package/dist/global-skills/expo-overview/references/launch-without-dev-menu.md +24 -0
  25. package/dist/global-skills/expo-project-structure/SKILL.md +2 -2
  26. package/dist/global-skills/expo-router/SKILL.md +2 -2
  27. package/dist/global-skills/expo-ui/SKILL.md +2 -2
  28. package/dist/global-skills/expo-upgrade/SKILL.md +2 -2
  29. package/dist/global-skills/expo-web-to-native/SKILL.md +2 -2
  30. package/dist/global-skills/expo-web-to-native/references/verify-on-device.md +1 -1
  31. package/dist/global-skills/firebase-app-hosting-basics/SKILL.md +16 -3
  32. package/dist/global-skills/firebase-app-hosting-basics/references/cli_commands.md +10 -12
  33. package/dist/global-skills/firebase-app-hosting-basics/references/configuration.md +2 -1
  34. package/dist/global-skills/firebase-basics/SKILL.md +6 -4
  35. package/dist/global-skills/firebase-basics/references/web_setup.md +3 -3
  36. package/dist/global-skills/instrument-feature-flags/references/adding-feature-flag-code.md +81 -333
  37. package/dist/global-skills/instrument-feature-flags/references/android.md +19 -13
  38. package/dist/global-skills/instrument-feature-flags/references/api.md +17 -13
  39. package/dist/global-skills/instrument-feature-flags/references/best-practices.md +35 -35
  40. package/dist/global-skills/instrument-feature-flags/references/django.md +120 -72
  41. package/dist/global-skills/instrument-feature-flags/references/dotnet.md +48 -102
  42. package/dist/global-skills/instrument-feature-flags/references/elixir.md +4 -10
  43. package/dist/global-skills/instrument-feature-flags/references/flask.md +55 -39
  44. package/dist/global-skills/instrument-feature-flags/references/flutter.md +19 -13
  45. package/dist/global-skills/instrument-feature-flags/references/go.md +21 -13
  46. package/dist/global-skills/instrument-feature-flags/references/ios.md +19 -13
  47. package/dist/global-skills/instrument-feature-flags/references/java.md +4 -12
  48. package/dist/global-skills/instrument-feature-flags/references/laravel.md +18 -16
  49. package/dist/global-skills/instrument-feature-flags/references/next-js.md +44 -54
  50. package/dist/global-skills/instrument-feature-flags/references/nodejs.md +21 -13
  51. package/dist/global-skills/instrument-feature-flags/references/php.md +21 -13
  52. package/dist/global-skills/instrument-feature-flags/references/python.md +22 -14
  53. package/dist/global-skills/instrument-feature-flags/references/react-native.md +18 -13
  54. package/dist/global-skills/instrument-feature-flags/references/react.md +21 -14
  55. package/dist/global-skills/instrument-feature-flags/references/ruby-on-rails.md +31 -68
  56. package/dist/global-skills/instrument-feature-flags/references/ruby.md +21 -13
  57. package/dist/global-skills/instrument-feature-flags/references/rust.md +10 -34
  58. package/dist/global-skills/instrument-feature-flags/references/usage.md +94 -114
  59. package/dist/global-skills/instrument-feature-flags/references/web.md +21 -13
  60. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-android.md +7 -7
  61. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-expo.md +2 -2
  62. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-fastapi.md +1 -1
  63. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-flask.md +1 -1
  64. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-laravel.md +2 -2
  65. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-php.md +1 -1
  66. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-python.md +1 -1
  67. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-ruby.md +1 -1
  68. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-swift.md +4 -4
  69. package/dist/global-skills/instrument-product-analytics/references/android.md +64 -109
  70. package/dist/global-skills/instrument-product-analytics/references/angular.md +32 -28
  71. package/dist/global-skills/instrument-product-analytics/references/astro.md +29 -34
  72. package/dist/global-skills/instrument-product-analytics/references/configuration.md +27 -42
  73. package/dist/global-skills/instrument-product-analytics/references/django.md +120 -72
  74. package/dist/global-skills/instrument-product-analytics/references/dotnet.md +48 -102
  75. package/dist/global-skills/instrument-product-analytics/references/elixir.md +25 -50
  76. package/dist/global-skills/instrument-product-analytics/references/flask.md +55 -39
  77. package/dist/global-skills/instrument-product-analytics/references/flutter.md +94 -96
  78. package/dist/global-skills/instrument-product-analytics/references/go.md +39 -77
  79. package/dist/global-skills/instrument-product-analytics/references/identify-users.md +24 -26
  80. package/dist/global-skills/instrument-product-analytics/references/ios.md +22 -22
  81. package/dist/global-skills/instrument-product-analytics/references/laravel.md +18 -16
  82. package/dist/global-skills/instrument-product-analytics/references/next-js.md +44 -54
  83. package/dist/global-skills/instrument-product-analytics/references/nuxt-js-3-6.md +32 -34
  84. package/dist/global-skills/instrument-product-analytics/references/nuxt-js.md +36 -36
  85. package/dist/global-skills/instrument-product-analytics/references/php.md +42 -87
  86. package/dist/global-skills/instrument-product-analytics/references/posthog-python.md +15 -12
  87. package/dist/global-skills/instrument-product-analytics/references/python.md +106 -154
  88. package/dist/global-skills/instrument-product-analytics/references/react-native.md +97 -176
  89. package/dist/global-skills/instrument-product-analytics/references/react-router-v6.md +29 -40
  90. package/dist/global-skills/instrument-product-analytics/references/react-router-v7-data-mode.md +29 -38
  91. package/dist/global-skills/instrument-product-analytics/references/react-router-v7-declarative-mode.md +29 -40
  92. package/dist/global-skills/instrument-product-analytics/references/react-router-v7-framework-mode.md +32 -37
  93. package/dist/global-skills/instrument-product-analytics/references/ruby-on-rails.md +31 -68
  94. package/dist/global-skills/instrument-product-analytics/references/ruby.md +40 -96
  95. package/dist/global-skills/instrument-product-analytics/references/svelte.md +34 -34
  96. package/dist/global-skills/instrument-product-analytics/references/tanstack-start.md +19 -19
  97. package/dist/global-skills/instrument-product-analytics/references/usage.md +94 -114
  98. package/dist/global-skills/instrument-product-analytics/references/vue-js.md +33 -45
  99. package/dist/global-skills/mongodb-search-and-ai/SKILL.md +40 -22
  100. package/dist/global-skills/mongodb-search-and-ai/references/automated-embedding.md +8 -2
  101. package/dist/global-skills/mongodb-search-and-ai/references/quick-start.md +725 -0
  102. package/dist/global-skills/mongodb-search-and-ai/scripts/quickstart_complete.py +386 -0
  103. package/dist/global-skills/neon/references/claimable-neon.md +2 -0
  104. package/dist/global-skills/neon-realtime/references/queries.md +2 -2
  105. package/dist/global-skills/resend/SKILL.md +1 -1
  106. package/dist/global-skills/resend/references/inboxes.md +49 -21
  107. package/dist/global-skills/resend/references/webhooks.md +1 -1
  108. package/dist/global-skills/stripe-best-practices/SKILL.md +1 -1
  109. package/dist/global-skills/stripe-best-practices/references/connect.md +1 -1
  110. package/dist/global-skills/stripe-best-practices/references/security.md +9 -9
  111. package/dist/global-skills/vybekiitReferences/backend/AGENTS.md +1 -1
  112. package/dist/global-skills/vybekiitReferences/backend/agent/goal-routes.json +303 -18
  113. package/dist/global-skills/vybekiitReferences/extension/AGENTS.md +1 -1
  114. package/dist/global-skills/vybekiitReferences/extension/agent/goal-routes.json +352 -18
  115. package/dist/global-skills/vybekiitReferences/mobile/AGENTS.md +1 -1
  116. package/dist/global-skills/vybekiitReferences/mobile/agent/goal-routes.json +413 -22
  117. package/dist/global-skills/vybekiitReferences/spa/AGENTS.md +1 -1
  118. package/dist/global-skills/vybekiitReferences/spa/agent/goal-routes.json +391 -21
  119. package/dist/global-skills/vybekiitReferences/web/AGENTS.md +14 -2
  120. package/dist/global-skills/vybekiitReferences/web/agent/goal-routes.json +490 -32
  121. package/package.json +8 -8
@@ -2,26 +2,54 @@
2
2
 
3
3
  # Python
4
4
 
5
+ Canonical URL: https://posthog.com/docs/libraries/python
6
+
7
+ > **Cloud region:** This page uses US Cloud hosts. For an EU Cloud project, use `https://eu.i.posthog.com` instead of `https://us.i.posthog.com` (ingestion host), and use `https://eu.posthog.com` instead of `https://app.posthog.com` (app host for links).
8
+
9
+ ## On this page
10
+
11
+ - [Installation](#installation)
12
+ - [Use the asyncio client](#use-the-asyncio-client)
13
+ - [Identifying users](#identifying-users)
14
+ - [Capturing events](#capturing-events)
15
+ - [Person profiles and properties](#person-profiles-and-properties)
16
+ - [Alias](#alias)
17
+ - [Contexts](#contexts)
18
+ - [Group analytics](#group-analytics)
19
+ - [Feature flags](#feature-flags)
20
+ - [Experiments (A/B tests)](#experiments-ab-tests)
21
+ - [AI Observability](#ai-observability)
22
+ - [Error tracking](#error-tracking)
23
+ - [Distributed tracing](#distributed-tracing)
24
+ - [GeoIP properties](#geoip-properties)
25
+ - [Debug mode](#debug-mode)
26
+ - [Disabling requests during tests](#disabling-requests-during-tests)
27
+ - [Connection configuration](#connection-configuration)
28
+ - [Filtering or modifying events before sending](#filtering-or-modifying-events-before-sending)
29
+ - [Historical migrations](#historical-migrations)
30
+ - [Serverless environments (Render/Lambda/...)](#serverless-environments-renderlambda)
31
+ - [Django](#django)
32
+ - [ASGI request middleware](#asgi-request-middleware)
33
+ - [Alternative name](#alternative-name)
34
+ - [Thank you](#thank-you)
35
+ - [Supported versions](#supported-versions)
36
+
5
37
  The Python SDK makes it easy to capture events, evaluate feature flags, track errors, and more in your Python apps.
6
38
 
7
39
  > These docs cover version `7.x` of the Python SDK, which requires Python 3.10 or higher. On Python 3.9? See [supported versions](#supported-versions).
8
40
 
9
41
  ## Installation
10
42
 
11
- Terminal
12
-
13
43
  ```bash
14
44
  pip install posthog
15
45
  ```
16
46
 
17
47
  **Upgrading to v6**
18
48
 
19
- Version `6.x` of the PostHog Python SDK introduces a new [contexts](/docs/libraries/python.md#contexts) API and breaking changes. If you're upgrading from `5.x` to `6.x`, read the [migration guide](/tutorials/python-v6-migration.md) first to learn more.
49
+ Version `6.x` of the PostHog Python SDK introduces a new [contexts](https://posthog.com/docs/libraries/python.md#contexts) API and breaking changes. If you're upgrading from `5.x` to `6.x`, read the [migration guide](https://posthog.com/tutorials/python-v6-migration.md) first to learn more.
20
50
 
21
51
  In your app, import the `posthog` library and set your project token and host **before** making any calls.
22
52
 
23
- Python
24
-
25
53
  ```python
26
54
  from posthog import Posthog
27
55
 
@@ -36,16 +64,12 @@ You can find your project token and instance address in the [project settings](h
36
64
 
37
65
  The Python SDK includes an asyncio-native client in version `7.45.0` and later. Continue to use `Posthog` in synchronous apps. For an asyncio app, install the optional async dependencies:
38
66
 
39
- Terminal
40
-
41
67
  ```bash
42
68
  pip install "posthog[async]>=7.45.0"
43
69
  ```
44
70
 
45
71
  Import `AsyncPosthog`, the customer-facing name for `AsyncClient`. Both names provide the same async context manager and lifecycle methods. Keep one client for the lifetime of your app. For example, use a FastAPI lifespan handler:
46
72
 
47
- Python
48
-
49
73
  ```python
50
74
  import os
51
75
  from contextlib import asynccontextmanager
@@ -72,8 +96,6 @@ Exiting the context calls `shutdown()`. This flushes buffered events, waits for
72
96
 
73
97
  `capture()` queues an event and returns without waiting for a network request. Don't await it:
74
98
 
75
- Python
76
-
77
99
  ```python
78
100
  posthog.capture(
79
101
  "event_name",
@@ -84,8 +106,6 @@ posthog.capture(
84
106
 
85
107
  Use `capture_immediate()` when your code must wait for that event's delivery attempt:
86
108
 
87
- Python
88
-
89
109
  ```python
90
110
  capture_id = await posthog.capture_immediate(
91
111
  "event_name",
@@ -97,8 +117,6 @@ capture_id = await posthog.capture_immediate(
97
117
 
98
118
  Await `evaluate_flags()` once, then use its snapshot with synchronous in-memory accessors. Pass the same snapshot to `capture()` to attach the exact values used for branching without another feature flag request:
99
119
 
100
- Python
101
-
102
120
  ```python
103
121
  flags = await posthog.evaluate_flags("user-distinct-id")
104
122
 
@@ -117,15 +135,13 @@ The snapshot provides synchronous `is_enabled()`, `get_flag()`, and `get_flag_pa
117
135
 
118
136
  ### Fetch remote config
119
137
 
120
- Initialize the client with a server-side [feature flags secure API key](/docs/feature-flags/remote-config.md#step-1-find-your-feature-flags-secure-api-key) as `secret_key`, then await the remote config request:
121
-
122
- Python
138
+ Initialize the client with a server-side [secret API key](https://posthog.com/docs/feature-flags/remote-config.md#step-1-get-a-secret-api-key) as `secret_key`, then await the remote config request:
123
139
 
124
140
  ```python
125
141
  config = await posthog.get_remote_config_payload("landing-page-config")
126
142
  ```
127
143
 
128
- See [Remote config](/docs/feature-flags/remote-config.md) for setup and security details.
144
+ See [Remote config](https://posthog.com/docs/feature-flags/remote-config.md) for setup and security details.
129
145
 
130
146
  ## Identifying users
131
147
 
@@ -133,8 +149,6 @@ See [Remote config](/docs/feature-flags/remote-config.md) for setup and security
133
149
  >
134
150
  > In Python, you can do this through a context. All event captures in the same context will be tagged automatically with the correct `distinct_id`. Typically, you would set a fresh context and identify at the top of each route.
135
151
  >
136
- > Python
137
- >
138
152
  > ```python
139
153
  > from posthog import new_context, identify_context, capture
140
154
  >
@@ -152,8 +166,6 @@ See [Remote config](/docs/feature-flags/remote-config.md) for setup and security
152
166
 
153
167
  You can send custom events using `capture`:
154
168
 
155
- Python
156
-
157
169
  ```python
158
170
  # Events captured with no context or explicit distinct_id are marked as personless and have an auto-generated distinct_id:
159
171
  posthog.capture('some-anon-event')
@@ -171,13 +183,11 @@ with new_context():
171
183
 
172
184
  > **Tip:** We recommend using a `[object] [verb]` format for your event names, where `[object]` is the entity that the behavior relates to, and `[verb]` is the behavior itself. For example, `project created`, `user signed up`, or `invite sent`.
173
185
 
174
- > **Tip:** You can define event schemas with typed properties and generate type-safe code using [schema management](/docs/product-analytics/schema-management.md).
186
+ > **Tip:** You can define event schemas with typed properties and generate type-safe code using [schema management](https://posthog.com/docs/product-analytics/schema-management.md).
175
187
 
176
188
  ### Setting event properties
177
189
 
178
- Optionally, you can include additional information with the event by including a [properties](/docs/data/events.md#event-properties) object:
179
-
180
- Python
190
+ Optionally, you can include additional information with the event by including a [properties](https://posthog.com/docs/data/events.md#event-properties) object:
181
191
 
182
192
  ```python
183
193
  posthog.capture(
@@ -194,17 +204,13 @@ posthog.capture(
194
204
 
195
205
  If you're aiming for a backend-only implementation of PostHog and won't be capturing events from your frontend, you can send `pageviews` from your backend like so:
196
206
 
197
- Python
198
-
199
207
  ```python
200
208
  posthog.capture('$pageview', distinct_id="distinct_id_of_the_user", properties={'$current_url': 'https://example.com'})
201
209
  ```
202
210
 
203
211
  ## Person profiles and properties
204
212
 
205
- The Python SDK captures identified events if the current context is identified or if you pass a distinct ID explicitly. These create [person profiles](/docs/data/persons.md). To set [person properties](/docs/product-analytics/person-properties.md) in these profiles, include them when capturing an event:
206
-
207
- Python
213
+ The Python SDK captures identified events if the current context is identified or if you pass a distinct ID explicitly. These create [person profiles](https://posthog.com/docs/data/persons.md). To set [person properties](https://posthog.com/docs/product-analytics/person-properties.md) in these profiles, include them when capturing an event:
208
214
 
209
215
  ```python
210
216
  # Passing a distinct id explicitly
@@ -224,11 +230,9 @@ with new_context():
224
230
  posthog.capture('event_name')
225
231
  ```
226
232
 
227
- For more details on the difference between `$set` and `$set_once`, see our [person properties docs](/docs/product-analytics/person-properties.md#what-is-the-difference-between-set-and-set_once).
228
-
229
- To capture [anonymous events](/docs/data/anonymous-vs-identified-events.md) without person profiles, set the event's `$process_person_profile` property to `False`. Events captured with no context or explicit distinct\_id are marked as personless, and will have an auto-generated distinct\_id:
233
+ For more details on the difference between `$set` and `$set_once`, see our [person properties docs](https://posthog.com/docs/product-analytics/person-properties.md#what-is-the-difference-between-set-and-set_once).
230
234
 
231
- Python
235
+ To capture [anonymous events](https://posthog.com/docs/data/anonymous-vs-identified-events.md) without person profiles, set the event's `$process_person_profile` property to `False`. Events captured with no context or explicit distinct\_id are marked as personless, and will have an auto-generated distinct\_id:
232
236
 
233
237
  ```python
234
238
  posthog.capture(
@@ -245,24 +249,20 @@ Sometimes, you want to assign multiple distinct IDs to a single user. This is he
245
249
 
246
250
  In this case, you can use `alias` to assign another distinct ID to the same user.
247
251
 
248
- Python
249
-
250
252
  ```python
251
253
  posthog.alias(previous_id='distinct_id', distinct_id='alias_id')
252
254
  ```
253
255
 
254
- We strongly recommend reading our docs on [alias](/docs/product-analytics/identify.md#alias-assigning-multiple-distinct-ids-to-the-same-user) to best understand how to correctly use this method.
256
+ We strongly recommend reading our docs on [alias](https://posthog.com/docs/product-analytics/identify.md#alias-assigning-multiple-distinct-ids-to-the-same-user) to best understand how to correctly use this method.
255
257
 
256
258
  ## Contexts
257
259
 
258
260
  The Python SDK uses nested contexts for managing state that's shared across events. Contexts are the recommended way to manage things like "which user is taking this action" (through `identify_context`), rather than manually passing user state through your apps stack.
259
261
 
260
- When events (including exceptions) are captured in a context, the event uses the user [distinct ID](/docs/getting-started/identify-users.md), [session ID](/docs/data/sessions.md), and tags that are (optionally) set in the context. This is useful for adding properties to multiple events during a single user's interaction with your product.
262
+ When events (including exceptions) are captured in a context, the event uses the user [distinct ID](https://posthog.com/docs/getting-started/identify-users.md), [session ID](https://posthog.com/docs/data/sessions.md), and tags that are (optionally) set in the context. This is useful for adding properties to multiple events during a single user's interaction with your product.
261
263
 
262
264
  You can enter a context using the `with` statement:
263
265
 
264
- Python
265
-
266
266
  ```python
267
267
  from posthog import new_context, tag, set_context_session, identify_context
268
268
 
@@ -283,8 +283,6 @@ with new_context():
283
283
 
284
284
  Contexts are persisted across function calls. If you enter one and then call a function and capture an event in the called function, it uses the context tags and session ID set in the parent context:
285
285
 
286
- Python
287
-
288
286
  ```python
289
287
  from posthog import new_context, tag
290
288
 
@@ -301,8 +299,6 @@ def outer_function():
301
299
 
302
300
  Contexts are nested, so tags added to a parent context are inherited by child contexts. If you set the same tag in both a parent and child context, the child context's value overrides the parent's at event capture (but the parent context won't be affected). This nesting also applies to session IDs and distinct IDs.
303
301
 
304
- Python
305
-
306
302
  ```python
307
303
  from posthog import new_context, tag
308
304
 
@@ -320,8 +316,6 @@ with new_context():
320
316
 
321
317
  You can disable this nesting behavior by passing `fresh=True` to `new_context`:
322
318
 
323
- Python
324
-
325
319
  ```python
326
320
  from posthog import new_context, tag
327
321
 
@@ -337,8 +331,6 @@ with new_context(fresh=True):
337
331
 
338
332
  Contexts can be associated with a distinct ID by calling `posthog.identify_context`:
339
333
 
340
- Python
341
-
342
334
  ```python
343
335
  from posthog import identify_context
344
336
 
@@ -347,8 +339,6 @@ identify_context("distinct-id")
347
339
 
348
340
  Within a context associated with a distinct ID, all events captured are associated with that user. You can override the distinct ID for a specific event by passing a `distinct_id` argument to `capture`:
349
341
 
350
- Python
351
-
352
342
  ```python
353
343
  from posthog import new_context, identify_context
354
344
 
@@ -360,14 +350,12 @@ with new_context():
360
350
 
361
351
  It's recommended to pass the currently active distinct ID from the frontend to the backend, using the `X-POSTHOG-DISTINCT-ID` header. If you're using our Django middleware, this is extracted and associated with the request handler context automatically.
362
352
 
363
- You can read more about identifying users in the [user identification documentation](/docs/product-analytics/identify.md).
353
+ You can read more about identifying users in the [user identification documentation](https://posthog.com/docs/product-analytics/identify.md).
364
354
 
365
355
  ### Contexts and sessions
366
356
 
367
357
  Contexts can be associated with a session ID by calling `posthog.set_context_session`. When linking backend events to frontend sessions, use the session ID from the frontend SDK (PostHog session IDs are UUIDv7 strings).
368
358
 
369
- Python
370
-
371
359
  ```python
372
360
  from posthog import new_context, set_context_session
373
361
  with new_context():
@@ -376,7 +364,7 @@ with new_context():
376
364
 
377
365
  **Using PostHog on your frontend too?**
378
366
 
379
- If you're using the PostHog JavaScript Web SDK on your frontend, it generates a session ID for you. Configure [`tracing_headers`](/docs/libraries/js/config.md#tracing-headers) for your backend hostname to add the session and distinct ID headers to browser requests automatically.
367
+ If you're using the PostHog JavaScript Web SDK on your frontend, it generates a session ID for you. Configure [`tracing_headers`](https://posthog.com/docs/libraries/js/config.md#tracing-headers) for your backend hostname to add the session and distinct ID headers to browser requests automatically.
380
368
 
381
369
  You need to extract the header in your request handler (if you're using our Django middleware integration, this happens automatically).
382
370
 
@@ -385,13 +373,11 @@ If you associate a context with a session, you'll be able to do things like:
385
373
  - See backend events on the session timeline when viewing session replays
386
374
  - View session replays for users that triggered a backend exception in error tracking
387
375
 
388
- You can read more about sessions in the [session tracking](/docs/data/sessions.md) documentation.
376
+ You can read more about sessions in the [session tracking](https://posthog.com/docs/data/sessions.md) documentation.
389
377
 
390
378
  ### Exception capture
391
379
 
392
- By default exceptions raised within a context are captured and available in the [error tracking](/docs/error-tracking.md) dashboard. You can override this behavior by passing `capture_exceptions=False` to `new_context`:
393
-
394
- Python
380
+ By default exceptions raised within a context are captured and available in the [error tracking](https://posthog.com/docs/error-tracking.md) dashboard. You can override this behavior by passing `capture_exceptions=False` to `new_context`:
395
381
 
396
382
  ```python
397
383
  from posthog import new_context, tag
@@ -410,8 +396,6 @@ with new_context(capture_exceptions=False):
410
396
 
411
397
  The SDK exposes a function decorator. It takes the same `fresh` and `capture_exceptions` arguments as `new_context` and provides a handy way to mark a whole function as being in a new context. For example:
412
398
 
413
- Python
414
-
415
399
  ```python
416
400
  from posthog import scoped, identify_context
417
401
 
@@ -424,22 +408,18 @@ def process_order(user, order_id):
424
408
 
425
409
  ## Group analytics
426
410
 
427
- Group analytics allows you to associate an event with a group (e.g. teams, organizations, etc.). Read the [Group Analytics](/docs/user-guides/group-analytics.md) guide for more information.
411
+ Group analytics allows you to associate an event with a group (e.g. teams, organizations, etc.). Read the [Group Analytics](https://posthog.com/docs/user-guides/group-analytics.md) guide for more information.
428
412
 
429
- > **Note:** This is a paid feature and is not available on the open-source or free cloud plan. Learn more on our [pricing page](/pricing.md).
413
+ > **Note:** This is a paid feature and is not available on the open-source or free cloud plan. Learn more on our [pricing page](https://posthog.com/pricing.md).
430
414
 
431
415
  To capture an event and associate it with a group:
432
416
 
433
- Python
434
-
435
417
  ```python
436
418
  posthog.capture('some_event', groups={'company': 'company_id_in_your_db'})
437
419
  ```
438
420
 
439
421
  To update properties on a group:
440
422
 
441
- Python
442
-
443
423
  ```python
444
424
  posthog.group_identify('company', 'company_id_in_your_db', {
445
425
  'name': 'Awesome Inc.',
@@ -453,7 +433,7 @@ The `name` is a special property which is used in the PostHog UI for the name of
453
433
 
454
434
  The examples in this section use the synchronous `Posthog` client. For `AsyncPosthog`, use the [awaited feature flag example](#evaluate-feature-flags). The returned snapshot uses the same accessors.
455
435
 
456
- PostHog's [feature flags](/docs/feature-flags.md) enable you to safely deploy and roll back new features as well as target specific users and groups with them.
436
+ PostHog's [feature flags](https://posthog.com/docs/feature-flags.md) enable you to safely deploy and roll back new features as well as target specific users and groups with them.
457
437
 
458
438
  There are two steps to implement feature flags in Python:
459
439
 
@@ -463,8 +443,6 @@ Call `posthog.evaluate_flags()` once for the user, then read values from the ret
463
443
 
464
444
  #### Boolean feature flags
465
445
 
466
- Python
467
-
468
446
  ```python
469
447
  flags = posthog.evaluate_flags("distinct_id_of_your_user")
470
448
 
@@ -476,8 +454,6 @@ if flags.is_enabled("flag-key"):
476
454
 
477
455
  #### Multivariate feature flags
478
456
 
479
- Python
480
-
481
457
  ```python
482
458
  flags = posthog.evaluate_flags("distinct_id_of_your_user")
483
459
 
@@ -495,9 +471,9 @@ if enabled_variant == "variant-key": # replace "variant-key" with the key of yo
495
471
 
496
472
  ### Step 2: Include feature flag information when capturing events
497
473
 
498
- If you want use your feature flag to breakdown or filter events in your [insights](/docs/product-analytics/insights.md), you'll need to include feature flag information in those events. This ensures that the feature flag value is attributed correctly to the event.
474
+ If you want use your feature flag to breakdown or filter events in your [insights](https://posthog.com/docs/product-analytics/insights.md), you'll need to include feature flag information in those events. This ensures that the feature flag value is attributed correctly to the event.
499
475
 
500
- > **Note:** This step is only required for events captured using our server-side SDKs or [API](/docs/api.md).
476
+ > **Note:** This step is only required for events captured using our server-side SDKs or [API](https://posthog.com/docs/api.md).
501
477
 
502
478
  There are two methods you can use to include feature flag information in your events:
503
479
 
@@ -505,8 +481,6 @@ There are two methods you can use to include feature flag information in your ev
505
481
 
506
482
  Pass the same `flags` object that you used for branching. This attaches the exact flag values from that evaluation and doesn't make another `/flags` request.
507
483
 
508
- Python
509
-
510
484
  ```python
511
485
  flags = posthog.evaluate_flags("distinct_id_of_your_user")
512
486
 
@@ -525,8 +499,6 @@ By default, this attaches every flag in the snapshot using `$feature/<flag-key>`
525
499
 
526
500
  To reduce event property bloat, pass a filtered snapshot:
527
501
 
528
- Python
529
-
530
502
  ```python
531
503
  # Attach only flags accessed with is_enabled() or get_flag() before this call
532
504
  posthog.capture(
@@ -549,8 +521,6 @@ posthog.capture(
549
521
 
550
522
  In the event properties, include `$feature/feature_flag_name: variant_key`:
551
523
 
552
- Python
553
-
554
524
  ```python
555
525
  posthog.capture(
556
526
  "event_name",
@@ -566,8 +536,6 @@ posthog.capture(
566
536
 
567
537
  By default, `posthog.evaluate_flags()` evaluates every flag for the user. If you only need a few flags, pass `flag_keys` to request only those flags:
568
538
 
569
- Python
570
-
571
539
  ```python
572
540
  flags = posthog.evaluate_flags(
573
541
  "distinct_id_of_your_user",
@@ -577,7 +545,7 @@ flags = posthog.evaluate_flags(
577
545
 
578
546
  ### Sending `$feature_flag_called` events
579
547
 
580
- Capturing `$feature_flag_called` events enables PostHog to know when a flag was accessed by a user and provide [analytics and insights](/docs/product-analytics/insights.md) on the flag. With `posthog.evaluate_flags()`, the SDK sends this event when you call `flags.is_enabled()` or `flags.get_flag()` for a flag.
548
+ Capturing `$feature_flag_called` events enables PostHog to know when a flag was accessed by a user and provide [analytics and insights](https://posthog.com/docs/product-analytics/insights.md) on the flag. With `posthog.evaluate_flags()`, the SDK sends this event when you call `flags.is_enabled()` or `flags.get_flag()` for a flag.
581
549
 
582
550
  The SDK deduplicates these events per `(distinct_id, flag, value)` in a local cache. If you reinitialize the PostHog client, the cache resets and `$feature_flag_called` events may be sent again. PostHog handles duplicates, so duplicate `$feature_flag_called` events don't affect your analytics.
583
551
 
@@ -585,14 +553,12 @@ The SDK deduplicates these events per `(distinct_id, flag, value)` in a local ca
585
553
 
586
554
  ### Advanced: Overriding server properties
587
555
 
588
- Sometimes, you may want to evaluate feature flags using [person properties](/docs/product-analytics/person-properties.md), [groups](/docs/product-analytics/group-analytics.md), or group properties that haven't been ingested yet, or were set incorrectly earlier.
556
+ Sometimes, you may want to evaluate feature flags using [person properties](https://posthog.com/docs/product-analytics/person-properties.md), [groups](https://posthog.com/docs/product-analytics/group-analytics.md), or group properties that haven't been ingested yet, or were set incorrectly earlier.
589
557
 
590
558
  You can provide properties to evaluate the flag with by using the `person properties`, `groups`, and `group properties` arguments. PostHog will then use these values to evaluate the flag, instead of any properties currently stored on your PostHog server.
591
559
 
592
560
  For example:
593
561
 
594
- Python
595
-
596
562
  ```python
597
563
  flags = posthog.evaluate_flags(
598
564
  "distinct_id_of_the_user",
@@ -642,8 +608,6 @@ Simply include any of these properties in the `person_properties` parameter alon
642
608
 
643
609
  You can configure the `feature_flags_request_timeout_seconds` parameter when initializing your PostHog client to set a flag request timeout. This helps prevent your code from being blocked if PostHog's servers are too slow to respond. By default, this is set to 3 seconds.
644
610
 
645
- Python
646
-
647
611
  ```python
648
612
  posthog = Posthog(
649
613
  "<ph_project_token>",
@@ -658,17 +622,15 @@ Evaluating feature flags requires making a request to PostHog for each flag. How
658
622
 
659
623
  It is best practice to use local evaluation flags when possible, since this enables you to resolve flags faster and with fewer API calls.
660
624
 
661
- For details on how to implement local evaluation, see our [local evaluation guide](/docs/feature-flags/local-evaluation.md).
625
+ For details on how to implement local evaluation, see our [local evaluation guide](https://posthog.com/docs/feature-flags/local-evaluation.md).
662
626
 
663
627
  #### Distributed environments
664
628
 
665
- In multi-worker or edge environments, you can implement custom caching for flag definitions using Redis, Cloudflare KV, or other storage backends. This enables sharing definitions across workers and coordinating fetches. See our guide for [local evaluation in distributed environments](/docs/feature-flags/local-evaluation/distributed-environments?tab=Python.md) for details.
629
+ In multi-worker or edge environments, you can implement custom caching for flag definitions using Redis, Cloudflare KV, or other storage backends. This enables sharing definitions across workers and coordinating fetches. See our guide for [local evaluation in distributed environments](https://posthog.com/docs/feature-flags/local-evaluation/distributed-environments?tab=Python.md) for details.
666
630
 
667
631
  ## Experiments (A/B tests)
668
632
 
669
- Since [experiments](/docs/experiments/start-here.md) use feature flags, the code for running an experiment is very similar to the feature flags code. This example uses the synchronous `Posthog` client:
670
-
671
- Python
633
+ Since [experiments](https://posthog.com/docs/experiments/start-here.md) use feature flags, the code for running an experiment is very similar to the feature flags code. This example uses the synchronous `Posthog` client:
672
634
 
673
635
  ```python
674
636
  flags = posthog.evaluate_flags("user_distinct_id")
@@ -680,17 +642,15 @@ if variant == "variant-name":
680
642
 
681
643
  With `AsyncPosthog`, await the evaluation: `flags = await posthog.evaluate_flags("user_distinct_id")`. The remaining snapshot access is the same.
682
644
 
683
- It's also possible to [run experiments without using feature flags](/docs/experiments/running-experiments-without-feature-flags.md).
645
+ It's also possible to [run experiments without using feature flags](https://posthog.com/docs/experiments/running-experiments-without-feature-flags.md).
684
646
 
685
647
  ## AI Observability
686
648
 
687
- Our Python SDK includes a built-in AI Observability feature. It enables you to capture LLM usage, performance, and more. Check out our [analytics docs](/docs/ai-observability.md) for more details on setting it up.
649
+ Our Python SDK includes a built-in AI Observability feature. It enables you to capture LLM usage, performance, and more. Check out our [analytics docs](https://posthog.com/docs/ai-observability.md) for more details on setting it up.
688
650
 
689
651
  ## Error tracking
690
652
 
691
- You can [autocapture exceptions](/docs/error-tracking/installation.md) by setting the `enable_exception_autocapture` argument to `True` when initializing the PostHog client.
692
-
693
- Python
653
+ You can [autocapture exceptions](https://posthog.com/docs/error-tracking/installation.md) by setting the `enable_exception_autocapture` argument to `True` when initializing the PostHog client.
694
654
 
695
655
  ```python
696
656
  from posthog import Posthog
@@ -700,8 +660,6 @@ posthog = Posthog("<ph_project_token>", enable_exception_autocapture=True, ...)
700
660
 
701
661
  You can also manually capture exceptions using the `capture_exception` method:
702
662
 
703
- Python
704
-
705
663
  ```python
706
664
  posthog.capture_exception(e, distinct_id='user_distinct_id', properties=additional_properties)
707
665
  ```
@@ -712,8 +670,6 @@ Contexts automatically capture exceptions thrown inside them, unless disable it
712
670
 
713
671
  The Python SDK can automatically capture the state of local variables when an exception occurs. This gives you a debugger-like view of your application state at the time of the error:
714
672
 
715
- Python
716
-
717
673
  ```python
718
674
  posthog = Posthog(
719
675
  "<ph_project_token>",
@@ -722,7 +678,7 @@ posthog = Posthog(
722
678
  )
723
679
  ```
724
680
 
725
- You can configure which variables are captured, masked, or ignored. See the [code variables documentation](/docs/error-tracking/code-variables/python.md) for detailed configuration options.
681
+ You can configure which variables are captured, masked, or ignored. See the [code variables documentation](https://posthog.com/docs/error-tracking/code-variables/python.md) for detailed configuration options.
726
682
 
727
683
  ## Distributed tracing
728
684
 
@@ -734,9 +690,7 @@ Tracing is new in the Python SDK and its API can still change in a minor release
734
690
 
735
691
  Tracing records **spans** – timed units of work – so you can see where time went in a request and how work fans out across your services. Spans created inside a [context](#contexts) automatically carry the person and session they belong to, so a slow trace links back to the person who experienced it.
736
692
 
737
- Tracing is off until you set the `traces` option. No OpenTelemetry dependency is required. For what you can do with spans once they arrive, see [Distributed tracing](/docs/distributed-tracing/start-here.md).
738
-
739
- Python
693
+ Tracing is off until you set the `traces` option. No OpenTelemetry dependency is required. For what you can do with spans once they arrive, see [Distributed tracing](https://posthog.com/docs/distributed-tracing/start-here.md).
740
694
 
741
695
  ```python
742
696
  from posthog import Posthog
@@ -758,8 +712,6 @@ Tracing is available on the synchronous `Posthog` client and the module-level AP
758
712
 
759
713
  `start_span` returns a span. Use it in a `with` block to make it the active span for the block and end it when the block exits. Spans started inside the block nest underneath it automatically.
760
714
 
761
- Python
762
-
763
715
  ```python
764
716
  with posthog.start_span("POST /checkout", kind="server") as span:
765
717
  span.set_attribute("plan", user.plan)
@@ -776,8 +728,6 @@ The recorded exception includes the stack trace, which contains file paths from
776
728
 
777
729
  For work that can't wrap a block, call `start_span` without `with`. **A span started this way isn't active**, so spans started afterwards aren't its children unless you pass `parent` explicitly – and you must call `end()` yourself.
778
730
 
779
- Python
780
-
781
731
  ```python
782
732
  span = posthog.start_span("background-sync", attributes={"queue": "emails"})
783
733
  try:
@@ -798,8 +748,6 @@ The active span is tracked with [`contextvars`](https://docs.python.org/3/librar
798
748
 
799
749
  Span names should be low-cardinality operation names – `GET /users/:id`, not `GET /users/123`. Variable values belong in attributes. Strings, booleans, integers, and floats keep their type. Lists and dictionaries are sent as arrays and maps, but PostHog stores them as serialized strings. Setting an attribute to `None` removes it, and any other value is converted to a string.
800
750
 
801
- Python
802
-
803
751
  ```python
804
752
  with posthog.start_span("GET /users/:id", kind="server") as span:
805
753
  span.set_attributes({"user.id": user_id, "db.rows": len(rows)})
@@ -867,9 +815,7 @@ A continued trace propagates the sampled flag it was handed, so a downstream sam
867
815
 
868
816
  ### Linking traces to people and sessions
869
817
 
870
- Spans created inside a [context](#contexts) that has a distinct ID or session ID automatically carry `posthogDistinctId` and `sessionId` attributes, which is what makes a trace reachable from a person or a Session Replay recording. In Django, the [contexts middleware](/docs/libraries/django.md#django-contexts-middleware) sets these for every request. Elsewhere, set them yourself:
871
-
872
- Python
818
+ Spans created inside a [context](#contexts) that has a distinct ID or session ID automatically carry `posthogDistinctId` and `sessionId` attributes, which is what makes a trace reachable from a person or a Session Replay recording. In Django, the [contexts middleware](https://posthog.com/docs/libraries/django.md#django-contexts-middleware) sets these for every request. Elsewhere, set them yourself:
873
819
 
874
820
  ```python
875
821
  from posthog import new_context, identify_context, set_context_session
@@ -888,8 +834,6 @@ Spans created outside a context with those values omit the attributes.
888
834
 
889
835
  `before_span_send` runs on every finished span before it's queued for export. It receives the span as a dict with `name`, `kind`, `status`, `attributes`, `events`, `start_time_ns`, `end_time_ns`, `trace_id`, `span_id`, and `parent_span_id`. Edit it and return it, or return `None` to drop the span entirely.
890
836
 
891
- Python
892
-
893
837
  ```python
894
838
  def scrub_spans(span):
895
839
  if span["attributes"].get("http.route") == "/health":
@@ -951,15 +895,15 @@ An invalid value falls back to its default with a warning. The exception is `bef
951
895
 
952
896
  Spans are exported on a background interval, even with `sync_mode` on. Both `flush()` and `shutdown()` export spans that have already ended. A span still open at `flush()` is exported once it ends; a span still open at `shutdown()` is discarded with a warning, so end your spans before shutting down – a `with` block does this for you. `shutdown()` gives queued spans up to 30 seconds to send.
953
897
 
954
- In a serverless handler, call `flush()` before returning. Events and spans are flushed concurrently, so it costs one round trip, not two.
955
-
956
- Python
898
+ If your serverless handler reuses a client across invocations, call `flush()` before returning. It flushes events and ended spans concurrently without stopping the client. Reserve `shutdown()` for a disposable client or process exit.
957
899
 
958
900
  ```python
959
901
  def handler(event, context):
960
- with posthog.start_span("handler"):
961
- do_work()
962
- posthog.flush()
902
+ try:
903
+ with posthog.start_span("handler"):
904
+ do_work()
905
+ finally:
906
+ posthog.flush()
963
907
  ```
964
908
 
965
909
  A script that exits without calling `shutdown()` still gets a brief best-effort flush at exit, but don't rely on it for spans you need.
@@ -972,8 +916,6 @@ As of posthog-python v3.0, the default now is to disregard the server IP, not ad
972
916
 
973
917
  You can go back to previous behavior by doing setting the `disable_geoip` argument in your initialization to `False`:
974
918
 
975
- Python
976
-
977
919
  ```python
978
920
  posthog = Posthog('api_key', disable_geoip=False)
979
921
  ```
@@ -990,8 +932,6 @@ The list of properties that this overrides:
990
932
 
991
933
  You can also explicitly chose to enable or disable GeoIP for a single capture request like so:
992
934
 
993
- Python
994
-
995
935
  ```python
996
936
  posthog.capture('test_event', disable_geoip=True|False)
997
937
  ```
@@ -1002,8 +942,6 @@ If you're not seeing the expected events being captured, the feature flags being
1002
942
 
1003
943
  You can enable debug mode by setting the `debug` option to `True` in the `PostHog` object. This will enable verbose logs about the inner workings of the SDK.
1004
944
 
1005
- Python
1006
-
1007
945
  ```python
1008
946
  posthog.debug = True
1009
947
  ```
@@ -1012,8 +950,6 @@ posthog.debug = True
1012
950
 
1013
951
  You can disable requests during tests by setting the `disabled` option to `True` in the `PostHog` object. This means no events will be captured or no requests will be sent to PostHog.
1014
952
 
1015
- Python
1016
-
1017
953
  ```python
1018
954
  if settings.TEST:
1019
955
  posthog.disabled = True
@@ -1029,8 +965,6 @@ You can configure connection behavior in several ways. The following settings sh
1029
965
 
1030
966
  TCP keepalive probes help prevent idle connections from being dropped by network infrastructure. This is the recommended approach for most cases where idle connections are terminated.
1031
967
 
1032
- Python
1033
-
1034
968
  ```python
1035
969
  import posthog
1036
970
 
@@ -1043,8 +977,6 @@ This enables TCP keepalive with sensible defaults (60 second idle time, 60 secon
1043
977
 
1044
978
  If you need each request to use a fresh connection, you can disable connection reuse entirely. This will incur additional overhead per request but may be desirable in some circumstances.
1045
979
 
1046
- Python
1047
-
1048
980
  ```python
1049
981
  import posthog
1050
982
 
@@ -1055,8 +987,6 @@ posthog.disable_connection_reuse()
1055
987
 
1056
988
  For advanced use cases, you can configure arbitrary socket options on the underlying HTTP connection.
1057
989
 
1058
- Python
1059
-
1060
990
  ```python
1061
991
  import socket
1062
992
  import posthog
@@ -1073,8 +1003,6 @@ Pass `None` to `set_socket_options()` to reset to default behavior.
1073
1003
 
1074
1004
  Use `before_send` to modify or drop events before they are queued for delivery. Return the modified event dictionary to send it, or `None` to drop it.
1075
1005
 
1076
- Python
1077
-
1078
1006
  ```python
1079
1007
  from typing import Any
1080
1008
 
@@ -1104,7 +1032,7 @@ If your callback raises an exception, the SDK logs the error and continues with
1104
1032
 
1105
1033
  ## Historical migrations
1106
1034
 
1107
- You can use the Python or Node SDK to run [historical migrations](/docs/migrate.md) of data into PostHog. To do so, set the `historical_migration` option to `true` when initializing the client.
1035
+ You can use the Python or Node SDK to run [historical migrations](https://posthog.com/docs/migrate.md) of data into PostHog. To do so, set the `historical_migration` option to `true` when initializing the client.
1108
1036
 
1109
1037
  ### Python
1110
1038
 
@@ -1181,12 +1109,13 @@ await client.shutdown()
1181
1109
 
1182
1110
  ### Synchronous `Posthog`
1183
1111
 
1184
- By default, the synchronous `Posthog` client buffers events before sending them to the capture endpoint. This can lead to lost events if the platform terminates the Python process before the buffer is fully flushed. To avoid this, you can either:
1112
+ By default, the synchronous `Posthog` client buffers events before sending them to the capture endpoint. A serverless platform can freeze or terminate the runtime before background delivery finishes.
1185
1113
 
1186
- - Call `posthog.shutdown()` before the process ends. This blocking call attempts to deliver queued events and cleans up the client.
1187
- - Enable `sync_mode` when initializing the client so each `posthog.capture()` call attempts delivery before it returns.
1114
+ - For a client shared across invocations, call `posthog.flush()` before the handler returns, including when application code throws. This blocking call attempts to deliver queued events without stopping the client. Its `timeout_seconds` argument defaults to `10` seconds.
1115
+ - For a disposable client or process exit, call `posthog.shutdown()` after its final work. It attempts to deliver queued events and cleans up the client. Don't reuse the client afterward. Unlike Node's `shutdown()`, Python's `shutdown()` takes no timeout argument.
1116
+ - Alternatively, enable `sync_mode` when initializing the client so each `posthog.capture()` call attempts delivery before it returns.
1188
1117
 
1189
- If you use [distributed tracing](#distributed-tracing), `sync_mode` doesn't apply to spans. Call `posthog.flush()` before the handler returns – see [Shutdown and short-lived processes](#shutdown-and-short-lived-processes).
1118
+ These calls do not guarantee that PostHog received every event. If you use [distributed tracing](#distributed-tracing), `sync_mode` doesn't apply to spans. For a shared client, call `posthog.flush()` before the handler returns – see [Shutdown and short-lived processes](#shutdown-and-short-lived-processes).
1190
1119
 
1191
1120
  ### Asyncio `AsyncPosthog`
1192
1121
 
@@ -1194,7 +1123,38 @@ Keep one `AsyncPosthog` client for the lifetime of your application. Use buffere
1194
1123
 
1195
1124
  ## Django
1196
1125
 
1197
- See our [Django docs](/docs/libraries/django.md) for how to set up PostHog in Django. Our library includes a [contexts middleware](/docs/libraries/django.md#django-contexts-middleware) that can automatically capture distinct IDs, session IDs, and other properties you can set up with tags.
1126
+ See our [Django docs](https://posthog.com/docs/libraries/django.md) for how to set up PostHog in Django. The [Django contexts middleware](https://posthog.com/docs/libraries/django.md#django-contexts-middleware) adds request properties and authenticated user identity to events captured during a request. You can also opt in to browser session and distinct ID headers.
1127
+
1128
+ ## ASGI request middleware
1129
+
1130
+ Version `7.69.0` and later of the Python SDK includes framework-independent ASGI middleware for FastAPI, Starlette, Litestar, and raw ASGI applications. It creates a PostHog context for each HTTP or WebSocket request, enriches captured events with request properties, and captures exceptions that escape the application.
1131
+
1132
+ For FastAPI or Starlette, add the middleware to your app:
1133
+
1134
+ ```python
1135
+ from fastapi import FastAPI
1136
+ from posthog import Posthog
1137
+ from posthog.integrations.asgi import PosthogASGIMiddleware
1138
+
1139
+ posthog = Posthog(
1140
+ "<ph_project_token>",
1141
+ host="https://us.i.posthog.com",
1142
+ enable_exception_autocapture=True,
1143
+ )
1144
+
1145
+ app = FastAPI()
1146
+ app.add_middleware(PosthogASGIMiddleware, client=posthog)
1147
+ ```
1148
+
1149
+ The middleware's `capture_exceptions` option defaults to `None`, which inherits the client's `enable_exception_autocapture` setting. Set it to `True` or `False` to override the client. Use `request_filter` to skip selected requests and `extra_properties` to add properties from the ASGI scope. Both callbacks can be synchronous or asynchronous.
1150
+
1151
+ The middleware ignores `X-PostHog-Distinct-ID` and `X-PostHog-Session-ID` by default because clients can control them. Set `trust_tracing_headers=True` only when you want to accept these headers as analytics context. Don't use them for authentication or authorization.
1152
+
1153
+ To instrument another ASGI framework or a raw ASGI app, wrap the application directly:
1154
+
1155
+ ```python
1156
+ app = PosthogASGIMiddleware(app, client=posthog)
1157
+ ```
1198
1158
 
1199
1159
  ## Alternative name
1200
1160
 
@@ -1211,11 +1171,3 @@ These docs cover version `7.x` of the PostHog Python SDK, which requires Python
1211
1171
  Everything on this page works the same way on `6.9.3`. Event capture, the context API (`new_context`, `identify_context`, `set_context_session`), and `PosthogContextMiddleware` are identical on `6.9.3` and `7.0.0` — `7.0.0` only dropped Python 3.9 and bumped the optional LLM provider SDKs. That includes the middleware identifying the request context from the `X-POSTHOG-DISTINCT-ID` header and falling back to the authenticated user, which behaves the same across both lines.
1212
1172
 
1213
1173
  Later `7.x` releases add what the 6.x line does not receive, such as the Celery integration, tracing header sanitization, and `set_context_device_id`. They also changed the middleware's own captured properties: `7.x` sends the request IP as `$ip`, where `6.9.3` sends it as `$ip_address`, and `7.x` additionally captures `$request_path`, `$raw_user_agent`, and the authenticated user's `email`.
1214
-
1215
- ### Still have questions?
1216
-
1217
- Ask PostHog AI
1218
-
1219
- ### Was this page useful?
1220
-
1221
- HelpfulCould be better