vybekiit 0.7.26 → 0.7.28

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 (274) hide show
  1. package/dist/bin.js +3910 -1300
  2. package/dist/global-guidance/language.md +443 -0
  3. package/dist/global-skills/add-ai/SKILL.md +1 -1
  4. package/dist/global-skills/add-analytics/SKILL.md +1 -1
  5. package/dist/global-skills/add-blog/SKILL.md +1 -1
  6. package/dist/global-skills/add-crud/SKILL.md +1 -1
  7. package/dist/global-skills/add-files/SKILL.md +1 -1
  8. package/dist/global-skills/add-images/SKILL.md +1 -1
  9. package/dist/global-skills/add-language/SKILL.md +1 -1
  10. package/dist/global-skills/add-notifications/SKILL.md +1 -1
  11. package/dist/global-skills/add-realtime/SKILL.md +1 -1
  12. package/dist/global-skills/add-route/SKILL.md +1 -1
  13. package/dist/global-skills/add-search/SKILL.md +1 -1
  14. package/dist/global-skills/add-signin/SKILL.md +1 -1
  15. package/dist/global-skills/add-teams/SKILL.md +1 -1
  16. package/dist/global-skills/add-upload/SKILL.md +1 -1
  17. package/dist/global-skills/aws-cdk/SKILL.md +19 -5
  18. package/dist/global-skills/aws-cdk/references/fast-deployments.md +191 -0
  19. package/dist/global-skills/aws-cdk/references/troubleshooting-deployment.md +16 -0
  20. package/dist/global-skills/aws-cloudformation/SKILL.md +16 -26
  21. package/dist/global-skills/aws-cloudformation/references/check-cloudformation-template-compliance.script.md +7 -3
  22. package/dist/global-skills/aws-cloudformation/references/cloudformation-language-server.md +177 -0
  23. package/dist/global-skills/aws-cloudformation/references/cloudformation-pre-deploy-validation.script.md +8 -2
  24. package/dist/global-skills/aws-cloudformation/references/persist-template-context.script.md +5 -8
  25. package/dist/global-skills/aws-cloudformation/references/retrieve-template-context.script.md +1 -1
  26. package/dist/global-skills/aws-cloudformation/references/security-considerations.md +51 -0
  27. package/dist/global-skills/aws-cloudformation/references/troubleshoot-failed-stack.script.md +138 -0
  28. package/dist/global-skills/aws-cloudformation/references/{validate-cloudformation-template.script.md → validate-with-cfn-lint.script.md} +15 -27
  29. package/dist/global-skills/aws-cloudformation/references/validate-with-cloudformation-validate.script.md +181 -0
  30. package/dist/global-skills/aws-cloudformation/references/validation-tool-selection.md +44 -0
  31. package/dist/global-skills/aws-serverless/SKILL.md +9 -1
  32. package/dist/global-skills/aws-serverless/references/architecture.md +3 -1
  33. package/dist/global-skills/aws-serverless/references/lambda.md +3 -1
  34. package/dist/global-skills/aws-serverless/references/orchestration.md +1 -0
  35. package/dist/global-skills/back-up-my-code/SKILL.md +1 -1
  36. package/dist/global-skills/better-auth-best-practices/SKILL.md +18 -8
  37. package/dist/global-skills/buy-domain/SKILL.md +1 -1
  38. package/dist/global-skills/check-safety/SKILL.md +1 -1
  39. package/dist/global-skills/configure-capabilities/SKILL.md +1 -1
  40. package/dist/global-skills/connect-account/SKILL.md +1 -1
  41. package/dist/global-skills/connect-account-backend/SKILL.md +1 -1
  42. package/dist/global-skills/design-my-data/SKILL.md +1 -1
  43. package/dist/global-skills/doctor/SKILL.md +1 -1
  44. package/dist/global-skills/eas-app-stores/SKILL.md +31 -15
  45. package/dist/global-skills/eas-app-stores/agents/openai.yaml +2 -2
  46. package/dist/global-skills/eas-app-stores/references/ios-app-store.md +37 -32
  47. package/dist/global-skills/eas-app-stores/references/native-ios.md +167 -0
  48. package/dist/global-skills/eas-app-stores/references/play-store.md +3 -7
  49. package/dist/global-skills/eas-app-stores/references/testflight.md +39 -35
  50. package/dist/global-skills/eas-simulator/SKILL.md +48 -26
  51. package/dist/global-skills/eas-simulator/references/controllers.md +32 -3
  52. package/dist/global-skills/eas-simulator/references/run-your-app.md +34 -4
  53. package/dist/global-skills/eas-simulator/references/troubleshooting.md +8 -4
  54. package/dist/global-skills/eas-update/SKILL.md +146 -0
  55. package/dist/global-skills/eas-update/agents/openai.yaml +4 -0
  56. package/dist/global-skills/expo-animation/RECIPES.md +2 -2
  57. package/dist/global-skills/expo-animation/SKILL.md +9 -2
  58. package/dist/global-skills/expo-brownfield/SKILL.md +18 -11
  59. package/dist/global-skills/expo-brownfield/agents/openai.yaml +2 -2
  60. package/dist/global-skills/expo-brownfield/references/brownfield-integrated.md +94 -69
  61. package/dist/global-skills/expo-brownfield/references/brownfield-isolated.md +40 -42
  62. package/dist/global-skills/expo-brownfield/references/comparison.md +5 -5
  63. package/dist/global-skills/expo-brownfield/references/feature-integration.md +163 -0
  64. package/dist/global-skills/expo-brownfield/references/troubleshooting.md +17 -17
  65. package/dist/global-skills/expo-brownfield/references/version-compatibility.md +40 -0
  66. package/dist/global-skills/expo-data-fetching/SKILL.md +27 -6
  67. package/dist/global-skills/expo-design-system/SKILL.md +27 -7
  68. package/dist/global-skills/expo-design-system/references/audit.md +7 -2
  69. package/dist/global-skills/expo-design-system/references/native-slop.md +74 -0
  70. package/dist/global-skills/expo-examples/SKILL.md +0 -1
  71. package/dist/global-skills/expo-examples/references/catalog.md +1 -1
  72. package/dist/global-skills/expo-migrate-module/SKILL.md +21 -10
  73. package/dist/global-skills/expo-migrate-module/references/compatibility.md +80 -23
  74. package/dist/global-skills/expo-migrate-module/references/migration-map.md +162 -11
  75. package/dist/global-skills/expo-native-ui/SKILL.md +25 -16
  76. package/dist/global-skills/expo-native-ui/agents/openai.yaml +2 -2
  77. package/dist/global-skills/expo-native-ui/references/controls.md +5 -46
  78. package/dist/global-skills/expo-native-ui/references/icons.md +21 -2
  79. package/dist/global-skills/expo-native-ui/references/media.md +15 -20
  80. package/dist/global-skills/expo-native-ui/references/visual-effects.md +12 -11
  81. package/dist/global-skills/expo-overview/SKILL.md +17 -12
  82. package/dist/global-skills/expo-router/SKILL.md +5 -3
  83. package/dist/global-skills/expo-router/references/tabs.md +5 -5
  84. package/dist/global-skills/expo-upgrade/SKILL.md +3 -1
  85. package/dist/global-skills/expo-web-to-native/references/false-friends.md +2 -2
  86. package/dist/global-skills/expo-web-to-native/references/native-patterns.md +1 -1
  87. package/dist/global-skills/firebase-ai-logic-basics/SKILL.md +13 -16
  88. package/dist/global-skills/firebase-ai-logic-basics/references/ios_setup.md +4 -5
  89. package/dist/global-skills/firebase-ai-logic-basics/references/usage_patterns_android.md +4 -4
  90. package/dist/global-skills/firebase-ai-logic-basics/references/usage_patterns_web.md +3 -3
  91. package/dist/global-skills/firebase-auth-basics/SKILL.md +11 -6
  92. package/dist/global-skills/firebase-auth-basics/references/client_sdk_android.md +4 -5
  93. package/dist/global-skills/firebase-auth-basics/references/client_sdk_web.md +3 -3
  94. package/dist/global-skills/firebase-auth-basics/references/flutter_setup.md +24 -25
  95. package/dist/global-skills/firebase-auth-basics/references/security_rules.md +4 -2
  96. package/dist/global-skills/firebase-crashlytics/references/android_setup.md +7 -4
  97. package/dist/global-skills/firebase-crashlytics/references/ios_setup.md +2 -3
  98. package/dist/global-skills/firebase-data-connect/SKILL.md +2 -1
  99. package/dist/global-skills/firebase-data-connect/examples.md +4 -4
  100. package/dist/global-skills/firebase-data-connect/reference/config.md +5 -4
  101. package/dist/global-skills/firebase-data-connect/reference/realtime.md +1 -2
  102. package/dist/global-skills/firebase-data-connect/reference/sdk_flutter.md +2 -2
  103. package/dist/global-skills/firebase-data-connect/reference/sdk_ios.md +2 -2
  104. package/dist/global-skills/firebase-data-connect/reference/sdk_web.md +17 -6
  105. package/dist/global-skills/firebase-data-connect/reference/security.md +5 -5
  106. package/dist/global-skills/firebase-data-connect/templates.md +2 -1
  107. package/dist/global-skills/firebase-firestore/SKILL.md +20 -8
  108. package/dist/global-skills/firebase-firestore/references/enterprise/android_sdk_usage.md +5 -4
  109. package/dist/global-skills/firebase-firestore/references/enterprise/data_model.md +12 -3
  110. package/dist/global-skills/firebase-firestore/references/enterprise/indexes.md +16 -18
  111. package/dist/global-skills/firebase-firestore/references/enterprise/provisioning.md +1 -1
  112. package/dist/global-skills/firebase-firestore/references/enterprise/python_sdk_usage.md +5 -1
  113. package/dist/global-skills/firebase-firestore/references/enterprise/web_sdk_usage.md +7 -7
  114. package/dist/global-skills/firebase-firestore/references/standard/android_sdk_usage.md +5 -5
  115. package/dist/global-skills/firebase-firestore/references/standard/flutter_setup.md +4 -4
  116. package/dist/global-skills/firebase-firestore/references/standard/indexes.md +16 -18
  117. package/dist/global-skills/firebase-firestore/references/standard/provisioning.md +1 -1
  118. package/dist/global-skills/firebase-remote-config-basics/SKILL.md +0 -5
  119. package/dist/global-skills/firebase-remote-config-basics/references/android_setup.md +36 -8
  120. package/dist/global-skills/firebase-remote-config-basics/references/ios_setup.md +1 -7
  121. package/dist/global-skills/firebase-security-rules-auditor/SKILL.md +17 -6
  122. package/dist/global-skills/{firebase-firestore/references/standard/security_rules.md → firestore-rules-creation/SKILL.md} +24 -13
  123. package/dist/global-skills/go-live/SKILL.md +1 -1
  124. package/dist/global-skills/grow-my-customers/SKILL.md +23 -0
  125. package/dist/global-skills/harden/SKILL.md +1 -1
  126. package/dist/global-skills/instrument-feature-flags/SKILL.md +25 -25
  127. package/dist/global-skills/instrument-feature-flags/references/adding-feature-flag-code.md +141 -285
  128. package/dist/global-skills/instrument-feature-flags/references/android.md +6 -15
  129. package/dist/global-skills/instrument-feature-flags/references/api.md +4 -11
  130. package/dist/global-skills/instrument-feature-flags/references/best-practices.md +1 -13
  131. package/dist/global-skills/instrument-feature-flags/references/django.md +14 -27
  132. package/dist/global-skills/instrument-feature-flags/references/dotnet.md +20 -79
  133. package/dist/global-skills/instrument-feature-flags/references/elixir.md +1 -9
  134. package/dist/global-skills/instrument-feature-flags/references/flask.md +13 -13
  135. package/dist/global-skills/instrument-feature-flags/references/flutter.md +3 -24
  136. package/dist/global-skills/instrument-feature-flags/references/go.md +3 -15
  137. package/dist/global-skills/instrument-feature-flags/references/ios.md +4 -17
  138. package/dist/global-skills/instrument-feature-flags/references/java.md +5 -13
  139. package/dist/global-skills/instrument-feature-flags/references/laravel.md +13 -17
  140. package/dist/global-skills/instrument-feature-flags/references/next-js.md +25 -32
  141. package/dist/global-skills/instrument-feature-flags/references/nodejs.md +8 -15
  142. package/dist/global-skills/instrument-feature-flags/references/php.md +1 -15
  143. package/dist/global-skills/instrument-feature-flags/references/python.md +2 -15
  144. package/dist/global-skills/instrument-feature-flags/references/react-native.md +13 -15
  145. package/dist/global-skills/instrument-feature-flags/references/react.md +17 -21
  146. package/dist/global-skills/instrument-feature-flags/references/ruby-on-rails.md +37 -83
  147. package/dist/global-skills/instrument-feature-flags/references/ruby.md +2 -15
  148. package/dist/global-skills/instrument-feature-flags/references/rust.md +13 -25
  149. package/dist/global-skills/instrument-feature-flags/references/usage.md +14 -63
  150. package/dist/global-skills/instrument-feature-flags/references/web.md +9 -14
  151. package/dist/global-skills/instrument-product-analytics/SKILL.md +29 -29
  152. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-astro-hybrid.md +3 -1
  153. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-astro-ssr.md +3 -1
  154. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-astro-static.md +3 -1
  155. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-astro-view-transitions.md +3 -1
  156. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-ruby-on-rails.md +3 -1
  157. package/dist/global-skills/instrument-product-analytics/references/android.md +72 -107
  158. package/dist/global-skills/instrument-product-analytics/references/angular.md +26 -28
  159. package/dist/global-skills/instrument-product-analytics/references/astro.md +13 -24
  160. package/dist/global-skills/instrument-product-analytics/references/configuration.md +45 -63
  161. package/dist/global-skills/instrument-product-analytics/references/django.md +14 -27
  162. package/dist/global-skills/instrument-product-analytics/references/dotnet.md +20 -79
  163. package/dist/global-skills/instrument-product-analytics/references/elixir.md +47 -49
  164. package/dist/global-skills/instrument-product-analytics/references/flask.md +13 -13
  165. package/dist/global-skills/instrument-product-analytics/references/flutter.md +60 -90
  166. package/dist/global-skills/instrument-product-analytics/references/go.md +17 -56
  167. package/dist/global-skills/instrument-product-analytics/references/identify-users.md +15 -15
  168. package/dist/global-skills/instrument-product-analytics/references/ios.md +11 -15
  169. package/dist/global-skills/instrument-product-analytics/references/laravel.md +13 -17
  170. package/dist/global-skills/instrument-product-analytics/references/next-js.md +25 -32
  171. package/dist/global-skills/instrument-product-analytics/references/nuxt-js-3-6.md +13 -27
  172. package/dist/global-skills/instrument-product-analytics/references/nuxt-js.md +14 -28
  173. package/dist/global-skills/instrument-product-analytics/references/php.md +33 -84
  174. package/dist/global-skills/instrument-product-analytics/references/posthog-python.md +229 -9
  175. package/dist/global-skills/instrument-product-analytics/references/python.md +415 -106
  176. package/dist/global-skills/instrument-product-analytics/references/react-native.md +161 -155
  177. package/dist/global-skills/instrument-product-analytics/references/react-router-v6.md +12 -33
  178. package/dist/global-skills/instrument-product-analytics/references/react-router-v7-data-mode.md +15 -33
  179. package/dist/global-skills/instrument-product-analytics/references/react-router-v7-declarative-mode.md +12 -33
  180. package/dist/global-skills/instrument-product-analytics/references/react-router-v7-framework-mode.md +26 -41
  181. package/dist/global-skills/instrument-product-analytics/references/ruby-on-rails.md +37 -83
  182. package/dist/global-skills/instrument-product-analytics/references/ruby.md +48 -108
  183. package/dist/global-skills/instrument-product-analytics/references/svelte.md +18 -24
  184. package/dist/global-skills/instrument-product-analytics/references/tanstack-start.md +17 -19
  185. package/dist/global-skills/instrument-product-analytics/references/usage.md +14 -63
  186. package/dist/global-skills/instrument-product-analytics/references/vue-js.md +29 -28
  187. package/dist/global-skills/manifest.json +8 -2
  188. package/dist/global-skills/mongodb-search-and-ai/SKILL.md +28 -37
  189. package/dist/global-skills/mongodb-search-and-ai/references/automated-embedding.md +438 -0
  190. package/dist/global-skills/mongodb-search-and-ai/references/hybrid-search.md +60 -4
  191. package/dist/global-skills/mongodb-search-and-ai/references/vector-search.md +46 -108
  192. package/dist/global-skills/neon/SKILL.md +207 -213
  193. package/dist/global-skills/neon/references/auth.md +12 -0
  194. package/dist/global-skills/neon/references/claimable-neon.md +10 -14
  195. package/dist/global-skills/neon/references/function-triggers.md +53 -0
  196. package/dist/global-skills/neon/references/logs-loki.md +61 -0
  197. package/dist/global-skills/neon/references/parse-env.md +32 -0
  198. package/dist/global-skills/neon/references/sdk.md +7 -0
  199. package/dist/global-skills/neon-ai-gateway/SKILL.md +14 -16
  200. package/dist/global-skills/neon-auth/SKILL.md +155 -0
  201. package/dist/global-skills/neon-auth/references/managed-auth.md +173 -0
  202. package/dist/global-skills/neon-auth/references/self-managed.md +25 -0
  203. package/dist/global-skills/neon-functions/SKILL.md +159 -84
  204. package/dist/global-skills/neon-functions/references/ai-sdk.md +4 -6
  205. package/dist/global-skills/neon-functions/references/function-triggers.md +249 -0
  206. package/dist/global-skills/neon-functions/references/mastra-studio.md +3 -3
  207. package/dist/global-skills/neon-functions/references/mcp.md +1 -1
  208. package/dist/global-skills/neon-functions/references/production-hardening.md +340 -0
  209. package/dist/global-skills/neon-functions/references/sse.md +8 -5
  210. package/dist/global-skills/neon-object-storage/SKILL.md +10 -11
  211. package/dist/global-skills/neon-postgres/SKILL.md +120 -17
  212. package/dist/global-skills/neon-postgres/references/full-text-search.md +99 -0
  213. package/dist/global-skills/neon-postgres/references/hybrid-search.md +90 -0
  214. package/dist/global-skills/neon-postgres/references/lakebase-search-drizzle.md +172 -0
  215. package/dist/global-skills/neon-postgres/references/vector-search.md +137 -0
  216. package/dist/global-skills/neon-postgres-branches/SKILL.md +3 -3
  217. package/dist/global-skills/neon-postgres-egress-optimizer/SKILL.md +1 -1
  218. package/dist/global-skills/onboarding/SKILL.md +16 -14
  219. package/dist/global-skills/plan-my-idea/SKILL.md +1 -1
  220. package/dist/global-skills/publish-app/SKILL.md +1 -1
  221. package/dist/global-skills/publish-extension/SKILL.md +1 -1
  222. package/dist/global-skills/resend/SKILL.md +4 -2
  223. package/dist/global-skills/resend/references/broadcasts.md +6 -1
  224. package/dist/global-skills/resend/references/receiving.md +29 -10
  225. package/dist/global-skills/resend/references/sending/email-management.md +14 -4
  226. package/dist/global-skills/resend/references/topics.md +9 -6
  227. package/dist/global-skills/resend/references/usage.md +117 -0
  228. package/dist/global-skills/resend/references/webhooks.md +59 -2
  229. package/dist/global-skills/reset-password/SKILL.md +1 -1
  230. package/dist/global-skills/save-data/SKILL.md +1 -1
  231. package/dist/global-skills/setup-email/SKILL.md +1 -1
  232. package/dist/global-skills/setup-payments/SKILL.md +1 -1
  233. package/dist/global-skills/setup-sms/SKILL.md +1 -1
  234. package/dist/global-skills/sign-in-with-email-link/SKILL.md +1 -1
  235. package/dist/global-skills/sign-in-with-google/SKILL.md +1 -1
  236. package/dist/global-skills/sign-in-with-phone/SKILL.md +1 -1
  237. package/dist/global-skills/stripe-best-practices/SKILL.md +35 -29
  238. package/dist/global-skills/stripe-best-practices/references/billing.md +9 -2
  239. package/dist/global-skills/stripe-best-practices/references/payments.md +4 -2
  240. package/dist/global-skills/stripe-best-practices/references/security.md +3 -1
  241. package/dist/global-skills/stripe-best-practices/references/tax.md +39 -20
  242. package/dist/global-skills/supabase/SKILL.md +6 -0
  243. package/dist/global-skills/track-errors/SKILL.md +1 -1
  244. package/dist/global-skills/update-kit/SKILL.md +1 -1
  245. package/dist/global-skills/use-railway/SKILL.md +42 -22
  246. package/dist/global-skills/use-railway/references/analyze-db.md +7 -6
  247. package/dist/global-skills/use-railway/references/cloud-agents.md +70 -0
  248. package/dist/global-skills/use-railway/references/configure.md +17 -2
  249. package/dist/global-skills/use-railway/references/databases.md +107 -0
  250. package/dist/global-skills/use-railway/references/deploy.md +5 -5
  251. package/dist/global-skills/use-railway/references/feature-flags.md +25 -13
  252. package/dist/global-skills/use-railway/references/iac.md +66 -77
  253. package/dist/global-skills/use-railway/references/operate.md +26 -3
  254. package/dist/global-skills/use-railway/references/request.md +31 -23
  255. package/dist/global-skills/use-railway/references/setup.md +16 -5
  256. package/dist/global-skills/use-railway/references/tracing.md +261 -0
  257. package/dist/global-skills/use-railway/references/usage.md +52 -0
  258. package/dist/global-skills/validate-my-idea/SKILL.md +54 -0
  259. package/dist/global-skills/{feedback → vybekiit-feedback}/SKILL.md +17 -13
  260. package/dist/global-skills/watch-my-app/SKILL.md +53 -0
  261. package/dist/global-skills/wire-auth/SKILL.md +1 -1
  262. package/dist/global-skills/wire-database/SKILL.md +1 -1
  263. package/dist/global-skills/wire-email/SKILL.md +1 -1
  264. package/dist/global-skills/wire-payments/SKILL.md +1 -1
  265. package/dist/global-skills/workers-best-practices/SKILL.md +36 -103
  266. package/dist/global-skills/workers-best-practices/references/configuration.md +139 -0
  267. package/dist/global-skills/workers-best-practices/references/platform-apis.md +51 -0
  268. package/dist/global-skills/workers-best-practices/references/{rules.md → runtime-patterns.md} +13 -137
  269. package/dist/global-skills/wrangler/SKILL.md +48 -901
  270. package/dist/global-skills/xcode-project-setup/scripts/xcode_spm_setup/Sources/main.swift +19 -15
  271. package/package.json +9 -8
  272. package/dist/global-skills/expo-native-ui/references/animations.md +0 -220
  273. package/dist/global-skills/firebase-firestore/references/enterprise/security_rules.md +0 -577
  274. package/dist/global-skills/workers-best-practices/references/review.md +0 -174
@@ -1,10 +1,6 @@
1
1
  > AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt
2
2
 
3
- # Python - Docs
4
-
5
- Copy page
6
-
7
- # Python - Docs
3
+ # Python
8
4
 
9
5
  The Python SDK makes it easy to capture events, evaluate feature flags, track errors, and more in your Python apps.
10
6
 
@@ -14,8 +10,6 @@ The Python SDK makes it easy to capture events, evaluate feature flags, track er
14
10
 
15
11
  Terminal
16
12
 
17
- PostHog AI
18
-
19
13
  ```bash
20
14
  pip install posthog
21
15
  ```
@@ -28,10 +22,9 @@ In your app, import the `posthog` library and set your project token and host **
28
22
 
29
23
  Python
30
24
 
31
- PostHog AI
32
-
33
25
  ```python
34
26
  from posthog import Posthog
27
+
35
28
  posthog = Posthog('<ph_project_token>', host='https://us.i.posthog.com')
36
29
  ```
37
30
 
@@ -39,6 +32,101 @@ posthog = Posthog('<ph_project_token>', host='https://us.i.posthog.com')
39
32
 
40
33
  You can find your project token and instance address in the [project settings](https://app.posthog.com/project/settings) page in PostHog.
41
34
 
35
+ ## Use the asyncio client
36
+
37
+ 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
+
39
+ Terminal
40
+
41
+ ```bash
42
+ pip install "posthog[async]>=7.45.0"
43
+ ```
44
+
45
+ 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
+
47
+ Python
48
+
49
+ ```python
50
+ import os
51
+ from contextlib import asynccontextmanager
52
+
53
+ from fastapi import FastAPI
54
+ from posthog import AsyncPosthog
55
+
56
+ @asynccontextmanager
57
+ async def lifespan(app: FastAPI):
58
+ async with AsyncPosthog(
59
+ os.environ["POSTHOG_PROJECT_TOKEN"],
60
+ host=os.environ["POSTHOG_HOST"],
61
+ secret_key=os.environ.get("POSTHOG_FEATURE_FLAGS_SECURE_API_KEY"),
62
+ ) as posthog:
63
+ app.state.posthog = posthog
64
+ yield
65
+
66
+ app = FastAPI(lifespan=lifespan)
67
+ ```
68
+
69
+ Exiting the context calls `shutdown()`. This flushes buffered events, waits for in-flight operations, stops the workers, and closes the HTTP transport. If you don't use the context manager, call `await posthog.shutdown()` during app shutdown. `await posthog.join()` has the same effect.
70
+
71
+ ### Capture events without blocking the event loop
72
+
73
+ `capture()` queues an event and returns without waiting for a network request. Don't await it:
74
+
75
+ Python
76
+
77
+ ```python
78
+ posthog.capture(
79
+ "event_name",
80
+ distinct_id="user-distinct-id",
81
+ properties={"source": "fastapi"},
82
+ )
83
+ ```
84
+
85
+ Use `capture_immediate()` when your code must wait for that event's delivery attempt:
86
+
87
+ Python
88
+
89
+ ```python
90
+ capture_id = await posthog.capture_immediate(
91
+ "event_name",
92
+ distinct_id="user-distinct-id",
93
+ )
94
+ ```
95
+
96
+ ### Evaluate feature flags
97
+
98
+ 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
+
100
+ Python
101
+
102
+ ```python
103
+ flags = await posthog.evaluate_flags("user-distinct-id")
104
+
105
+ if flags.is_enabled("new-checkout"):
106
+ # Show the new checkout
107
+ pass
108
+
109
+ posthog.capture(
110
+ "checkout started",
111
+ distinct_id="user-distinct-id",
112
+ flags=flags,
113
+ )
114
+ ```
115
+
116
+ The snapshot provides synchronous `is_enabled()`, `get_flag()`, and `get_flag_payload()` accessors. The awaited `evaluate_flags()` call also accepts `groups`, `person_properties`, `group_properties`, `disable_geoip`, `flag_keys`, and `device_id` arguments.
117
+
118
+ ### Fetch remote config
119
+
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
123
+
124
+ ```python
125
+ config = await posthog.get_remote_config_payload("landing-page-config")
126
+ ```
127
+
128
+ See [Remote config](/docs/feature-flags/remote-config.md) for setup and security details.
129
+
42
130
  ## Identifying users
43
131
 
44
132
  > **Identifying users is required.** Backend events need a `distinct_id` to associate events with the correct user.
@@ -47,10 +135,9 @@ You can find your project token and instance address in the [project settings](h
47
135
  >
48
136
  > Python
49
137
  >
50
- > PostHog AI
51
- >
52
138
  > ```python
53
139
  > from posthog import new_context, identify_context, capture
140
+ >
54
141
  > @app.get("/foo")
55
142
  > def foo(current_user: User = Depends(get_current_user)):
56
143
  > with new_context(): # Set context at the top of a route
@@ -67,12 +154,12 @@ You can send custom events using `capture`:
67
154
 
68
155
  Python
69
156
 
70
- PostHog AI
71
-
72
157
  ```python
73
158
  # Events captured with no context or explicit distinct_id are marked as personless and have an auto-generated distinct_id:
74
159
  posthog.capture('some-anon-event')
160
+
75
161
  from posthog import identify_context, new_context
162
+
76
163
  # Use contexts to manage user identification across multiple capture calls
77
164
  with new_context():
78
165
  identify_context('distinct_id_of_the_user')
@@ -92,8 +179,6 @@ Optionally, you can include additional information with the event by including a
92
179
 
93
180
  Python
94
181
 
95
- PostHog AI
96
-
97
182
  ```python
98
183
  posthog.capture(
99
184
  "user_signed_up",
@@ -111,20 +196,16 @@ If you're aiming for a backend-only implementation of PostHog and won't be captu
111
196
 
112
197
  Python
113
198
 
114
- PostHog AI
115
-
116
199
  ```python
117
200
  posthog.capture('$pageview', distinct_id="distinct_id_of_the_user", properties={'$current_url': 'https://example.com'})
118
201
  ```
119
202
 
120
203
  ## Person profiles and properties
121
204
 
122
- 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/data/user-properties.md) in these profiles, include them when capturing an event:
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:
123
206
 
124
207
  Python
125
208
 
126
- PostHog AI
127
-
128
209
  ```python
129
210
  # Passing a distinct id explicitly
130
211
  posthog.capture(
@@ -135,6 +216,7 @@ posthog.capture(
135
216
  '$set_once': {'initial_url': '/blog'}
136
217
  }
137
218
  )
219
+
138
220
  # Using contexts
139
221
  from posthog import new_context, identify_context
140
222
  with new_context():
@@ -142,14 +224,12 @@ with new_context():
142
224
  posthog.capture('event_name')
143
225
  ```
144
226
 
145
- For more details on the difference between `$set` and `$set_once`, see our [person properties docs](/docs/data/user-properties.md#what-is-the-difference-between-set-and-set_once).
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).
146
228
 
147
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:
148
230
 
149
231
  Python
150
232
 
151
- PostHog AI
152
-
153
233
  ```python
154
234
  posthog.capture(
155
235
  event='event_name',
@@ -167,8 +247,6 @@ In this case, you can use `alias` to assign another distinct ID to the same user
167
247
 
168
248
  Python
169
249
 
170
- PostHog AI
171
-
172
250
  ```python
173
251
  posthog.alias(previous_id='distinct_id', distinct_id='alias_id')
174
252
  ```
@@ -185,18 +263,20 @@ You can enter a context using the `with` statement:
185
263
 
186
264
  Python
187
265
 
188
- PostHog AI
189
-
190
266
  ```python
191
267
  from posthog import new_context, tag, set_context_session, identify_context
268
+
192
269
  with new_context():
193
270
  tag("transaction_id", "abc123")
194
271
  tag("some_arbitrary_value", {"tags": "can be dicts"})
272
+
195
273
  # Sessions are UUIDv7 values and used to track a sequence of events that occur within a single user session
196
274
  # See https://posthog.com/docs/data/sessions
197
275
  set_context_session(session_id)
276
+
198
277
  # Setting the context-level distinct ID. See below for more details.
199
278
  identify_context(user_id)
279
+
200
280
  # This event is captured with the distinct ID, session ID, and tags set above
201
281
  posthog.capture("order_processed")
202
282
  ```
@@ -205,13 +285,14 @@ Contexts are persisted across function calls. If you enter one and then call a f
205
285
 
206
286
  Python
207
287
 
208
- PostHog AI
209
-
210
288
  ```python
211
289
  from posthog import new_context, tag
290
+
212
291
  def some_function():
213
292
  # When called from `outer_function`, this event is captured with the property some-key="value-4"
214
293
  posthog.capture("order_processed")
294
+
295
+
215
296
  def outer_function():
216
297
  with new_context():
217
298
  tag("some-key", "value-4")
@@ -222,10 +303,9 @@ Contexts are nested, so tags added to a parent context are inherited by child co
222
303
 
223
304
  Python
224
305
 
225
- PostHog AI
226
-
227
306
  ```python
228
307
  from posthog import new_context, tag
308
+
229
309
  with new_context():
230
310
  tag("some-key", "value-1")
231
311
  tag("some-other-key", "another-value")
@@ -233,6 +313,7 @@ with new_context():
233
313
  tag("some-key", "value-2")
234
314
  # This event is captured with some-key="value-2" and some-other-key="another-value"
235
315
  posthog.capture("order_processed")
316
+
236
317
  # This event is captured with some-key="value-1" and some-other-key="another-value"
237
318
  posthog.capture("order_processed")
238
319
  ```
@@ -241,10 +322,9 @@ You can disable this nesting behavior by passing `fresh=True` to `new_context`:
241
322
 
242
323
  Python
243
324
 
244
- PostHog AI
245
-
246
325
  ```python
247
326
  from posthog import new_context, tag
327
+
248
328
  with new_context(fresh=True):
249
329
  tag("some-key", "value-2")
250
330
  # This event only has the property some-key="value-2" from the fresh context
@@ -259,10 +339,9 @@ Contexts can be associated with a distinct ID by calling `posthog.identify_conte
259
339
 
260
340
  Python
261
341
 
262
- PostHog AI
263
-
264
342
  ```python
265
343
  from posthog import identify_context
344
+
266
345
  identify_context("distinct-id")
267
346
  ```
268
347
 
@@ -270,10 +349,9 @@ Within a context associated with a distinct ID, all events captured are associat
270
349
 
271
350
  Python
272
351
 
273
- PostHog AI
274
-
275
352
  ```python
276
353
  from posthog import new_context, identify_context
354
+
277
355
  with new_context():
278
356
  identify_context("distinct-id")
279
357
  posthog.capture("order_processed") # will be associated with distinct-id
@@ -290,8 +368,6 @@ Contexts can be associated with a session ID by calling `posthog.set_context_ses
290
368
 
291
369
  Python
292
370
 
293
- PostHog AI
294
-
295
371
  ```python
296
372
  from posthog import new_context, set_context_session
297
373
  with new_context():
@@ -317,13 +393,13 @@ By default exceptions raised within a context are captured and available in the
317
393
 
318
394
  Python
319
395
 
320
- PostHog AI
321
-
322
396
  ```python
323
397
  from posthog import new_context, tag
398
+
324
399
  with new_context(capture_exceptions=False):
325
400
  tag("transaction_id", "abc123")
326
401
  tag("some_arbitrary_value", {"tags": "can be dicts"})
402
+
327
403
  # This event will be captured with the tags set above
328
404
  posthog.capture("order_processed")
329
405
  # This exception will not be captured
@@ -336,10 +412,9 @@ The SDK exposes a function decorator. It takes the same `fresh` and `capture_exc
336
412
 
337
413
  Python
338
414
 
339
- PostHog AI
340
-
341
415
  ```python
342
416
  from posthog import scoped, identify_context
417
+
343
418
  @scoped(fresh=True)
344
419
  def process_order(user, order_id):
345
420
  identify_context(user.distinct_id)
@@ -357,8 +432,6 @@ To capture an event and associate it with a group:
357
432
 
358
433
  Python
359
434
 
360
- PostHog AI
361
-
362
435
  ```python
363
436
  posthog.capture('some_event', groups={'company': 'company_id_in_your_db'})
364
437
  ```
@@ -367,8 +440,6 @@ To update properties on a group:
367
440
 
368
441
  Python
369
442
 
370
- PostHog AI
371
-
372
443
  ```python
373
444
  posthog.group_identify('company', 'company_id_in_your_db', {
374
445
  'name': 'Awesome Inc.',
@@ -380,6 +451,8 @@ The `name` is a special property which is used in the PostHog UI for the name of
380
451
 
381
452
  ## Feature flags
382
453
 
454
+ 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
+
383
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.
384
457
 
385
458
  There are two steps to implement feature flags in Python:
@@ -392,10 +465,9 @@ Call `posthog.evaluate_flags()` once for the user, then read values from the ret
392
465
 
393
466
  Python
394
467
 
395
- PostHog AI
396
-
397
468
  ```python
398
469
  flags = posthog.evaluate_flags("distinct_id_of_your_user")
470
+
399
471
  if flags.is_enabled("flag-key"):
400
472
  # Do something differently for this user
401
473
  # Optional: fetch the payload
@@ -406,11 +478,11 @@ if flags.is_enabled("flag-key"):
406
478
 
407
479
  Python
408
480
 
409
- PostHog AI
410
-
411
481
  ```python
412
482
  flags = posthog.evaluate_flags("distinct_id_of_your_user")
483
+
413
484
  enabled_variant = flags.get_flag("flag-key")
485
+
414
486
  if enabled_variant == "variant-key": # replace "variant-key" with the key of your variant
415
487
  # Do something differently for this user
416
488
  # Optional: fetch the payload
@@ -435,13 +507,13 @@ Pass the same `flags` object that you used for branching. This attaches the exac
435
507
 
436
508
  Python
437
509
 
438
- PostHog AI
439
-
440
510
  ```python
441
511
  flags = posthog.evaluate_flags("distinct_id_of_your_user")
512
+
442
513
  if flags.is_enabled("flag-key"):
443
514
  # Do something differently for this user
444
515
  pass
516
+
445
517
  posthog.capture(
446
518
  "event_name",
447
519
  distinct_id="distinct_id_of_your_user",
@@ -455,8 +527,6 @@ To reduce event property bloat, pass a filtered snapshot:
455
527
 
456
528
  Python
457
529
 
458
- PostHog AI
459
-
460
530
  ```python
461
531
  # Attach only flags accessed with is_enabled() or get_flag() before this call
462
532
  posthog.capture(
@@ -464,6 +534,7 @@ posthog.capture(
464
534
  distinct_id="distinct_id_of_your_user",
465
535
  flags=flags.only_accessed(),
466
536
  )
537
+
467
538
  # Attach only specific flags
468
539
  posthog.capture(
469
540
  "event_name",
@@ -480,8 +551,6 @@ In the event properties, include `$feature/feature_flag_name: variant_key`:
480
551
 
481
552
  Python
482
553
 
483
- PostHog AI
484
-
485
554
  ```python
486
555
  posthog.capture(
487
556
  "event_name",
@@ -499,8 +568,6 @@ By default, `posthog.evaluate_flags()` evaluates every flag for the user. If you
499
568
 
500
569
  Python
501
570
 
502
- PostHog AI
503
-
504
571
  ```python
505
572
  flags = posthog.evaluate_flags(
506
573
  "distinct_id_of_your_user",
@@ -526,8 +593,6 @@ For example:
526
593
 
527
594
  Python
528
595
 
529
- PostHog AI
530
-
531
596
  ```python
532
597
  flags = posthog.evaluate_flags(
533
598
  "distinct_id_of_the_user",
@@ -541,6 +606,7 @@ flags = posthog.evaluate_flags(
541
606
  "another_group_type": {"group_property_name": "value"},
542
607
  },
543
608
  )
609
+
544
610
  if flags.is_enabled("flag-key"):
545
611
  # Do something differently for this user
546
612
  ```
@@ -578,8 +644,6 @@ You can configure the `feature_flags_request_timeout_seconds` parameter when ini
578
644
 
579
645
  Python
580
646
 
581
- PostHog AI
582
-
583
647
  ```python
584
648
  posthog = Posthog(
585
649
  "<ph_project_token>",
@@ -602,19 +666,20 @@ In multi-worker or edge environments, you can implement custom caching for flag
602
666
 
603
667
  ## Experiments (A/B tests)
604
668
 
605
- Since [experiments](/docs/experiments/start-here.md) use feature flags, the code for running an experiment is very similar to the feature flags code:
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:
606
670
 
607
671
  Python
608
672
 
609
- PostHog AI
610
-
611
673
  ```python
612
674
  flags = posthog.evaluate_flags("user_distinct_id")
613
675
  variant = flags.get_flag("experiment-feature-flag-key")
676
+
614
677
  if variant == "variant-name":
615
678
  # Do something
616
679
  ```
617
680
 
681
+ With `AsyncPosthog`, await the evaluation: `flags = await posthog.evaluate_flags("user_distinct_id")`. The remaining snapshot access is the same.
682
+
618
683
  It's also possible to [run experiments without using feature flags](/docs/experiments/running-experiments-without-feature-flags.md).
619
684
 
620
685
  ## AI Observability
@@ -627,10 +692,9 @@ You can [autocapture exceptions](/docs/error-tracking/installation.md) by settin
627
692
 
628
693
  Python
629
694
 
630
- PostHog AI
631
-
632
695
  ```python
633
696
  from posthog import Posthog
697
+
634
698
  posthog = Posthog("<ph_project_token>", enable_exception_autocapture=True, ...)
635
699
  ```
636
700
 
@@ -638,8 +702,6 @@ You can also manually capture exceptions using the `capture_exception` method:
638
702
 
639
703
  Python
640
704
 
641
- PostHog AI
642
-
643
705
  ```python
644
706
  posthog.capture_exception(e, distinct_id='user_distinct_id', properties=additional_properties)
645
707
  ```
@@ -652,8 +714,6 @@ The Python SDK can automatically capture the state of local variables when an ex
652
714
 
653
715
  Python
654
716
 
655
- PostHog AI
656
-
657
717
  ```python
658
718
  posthog = Posthog(
659
719
  "<ph_project_token>",
@@ -664,6 +724,246 @@ posthog = Posthog(
664
724
 
665
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.
666
726
 
727
+ ## Distributed tracing
728
+
729
+ > Requires `posthog` version 7.58.0 or later.
730
+
731
+ **The span API is experimental**
732
+
733
+ Tracing is new in the Python SDK and its API can still change in a minor release. Spans you send are kept – it's the SDK surface that isn't frozen yet.
734
+
735
+ 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
+
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
740
+
741
+ ```python
742
+ from posthog import Posthog
743
+
744
+ posthog = Posthog(
745
+ "<ph_project_token>",
746
+ host="https://us.i.posthog.com",
747
+ traces={"service_name": "checkout-api"},
748
+ )
749
+ ```
750
+
751
+ If you use the module-level API instead, set `posthog.traces = {"service_name": "checkout-api"}` alongside your other options, before you start the first span.
752
+
753
+ Set `service_name` – PostHog groups operations by service and span name.
754
+
755
+ Tracing is available on the synchronous `Posthog` client and the module-level API. `AsyncPosthog` doesn't support it yet.
756
+
757
+ ### Creating spans
758
+
759
+ `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
+
761
+ Python
762
+
763
+ ```python
764
+ with posthog.start_span("POST /checkout", kind="server") as span:
765
+ span.set_attribute("plan", user.plan)
766
+
767
+ with posthog.start_span("create-order"):
768
+ order = create_order(cart)
769
+ with posthog.start_span("charge-card"):
770
+ stripe.charge(order)
771
+ ```
772
+
773
+ If an exception escapes the block, the span records it, its status is set to `error`, and the exception propagates unchanged. `KeyboardInterrupt`, `GeneratorExit`, and `asyncio.CancelledError` still end the span but aren't recorded as failures.
774
+
775
+ The recorded exception includes the stack trace, which contains file paths from your server. If you'd rather those didn't leave your process, remove `exception.stacktrace` in [`before_span_send`](#scrubbing-and-dropping-spans).
776
+
777
+ 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
+
779
+ Python
780
+
781
+ ```python
782
+ span = posthog.start_span("background-sync", attributes={"queue": "emails"})
783
+ try:
784
+ # Explicitly parent a child to a span that isn't active.
785
+ child = posthog.start_span("send-batch", parent=span)
786
+ child.end()
787
+ finally:
788
+ span.end()
789
+ ```
790
+
791
+ `get_active_span()` returns the span active in the current context, or `None` when there isn't one.
792
+
793
+ The active span is tracked with [`contextvars`](https://docs.python.org/3/library/contextvars.html), so it carries across `await` in asyncio code. Threads don't reliably inherit it – pass `parent=span` to continue the trace in a thread you start or a `ThreadPoolExecutor` task. A forked child process starts with no active span, so pass `parent` there too.
794
+
795
+ `start_span` always returns a usable span, even when tracing is off, so your code never needs to check whether tracing is enabled.
796
+
797
+ ### Span names and attributes
798
+
799
+ 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
+
801
+ Python
802
+
803
+ ```python
804
+ with posthog.start_span("GET /users/:id", kind="server") as span:
805
+ span.set_attributes({"user.id": user_id, "db.rows": len(rows)})
806
+ span.add_event("cache-miss")
807
+
808
+ if not rows:
809
+ span.set_status("error", "user not found")
810
+ ```
811
+
812
+ | Method | Description |
813
+ | --- | --- |
814
+ | `set_attribute(key, value)` | Set a single attribute |
815
+ | `set_attributes(attributes)` | Merge several attributes at once |
816
+ | `add_event(name, attributes=None, timestamp=None)` | Record a timestamped event within the span |
817
+ | `set_status(code, message=None)` | Set the outcome: `"ok"` or `"error"`. `"ok"` is final – an exception raised later in the `with` block doesn't override it |
818
+ | `record_exception(exception)` | Attach an exception event carrying the type, message, and – for a raised exception – stack trace, and set status to `error`. Use it for exceptions you catch and handle |
819
+ | `update_name(name)` | Replace the span name, e.g. once a route template resolves |
820
+ | `traceparent()` | This span's W3C `traceparent` header value, or `None` |
821
+ | `tracestate()` | This span's W3C `tracestate` value, or `None` when it has none |
822
+ | `end(end_time=None)` | End the span and queue it for export. A `with` block does this for you |
823
+
824
+ Every method except `traceparent()`, `tracestate()`, and `end()` returns the span, so calls chain. Calls after `end()` are ignored.
825
+
826
+ `start_span` takes these keyword arguments:
827
+
828
+ | Argument | Description |
829
+ | --- | --- |
830
+ | `kind` | What the work is: `"internal"` (default), `"server"` for an inbound request, `"client"` for an outbound call, `"producer"` or `"consumer"` for queue work |
831
+ | `attributes` | Attributes to set at span start |
832
+ | `parent` | A span, or an inbound W3C `traceparent` string to continue a trace another service started. Defaults to the active span |
833
+ | `tracestate` | The W3C `tracestate` accompanying a `traceparent` string. Ignored when `parent` is a span, which inherits its parent's |
834
+ | `start_time` | Backdate the span's start, as a `datetime` or seconds since the epoch. The server clamps a start more than 24 hours old to receive time; with `debug` on, the SDK prints a debug message when you pass one |
835
+
836
+ ### Tracing across services
837
+
838
+ Spans use [W3C Trace Context](https://www.w3.org/TR/trace-context/), so a trace can span several services. Pass an inbound `traceparent` header as `parent` to continue a trace another service started, and send `span.traceparent()` onward when you call out.
839
+
840
+ app.py
841
+
842
+ ```python
843
+ import requests
844
+ from flask import request
845
+
846
+
847
+ @app.post("/checkout")
848
+ def checkout():
849
+ with posthog.start_span(
850
+ "POST /checkout",
851
+ kind="server",
852
+ parent=request.headers.get("traceparent"),
853
+ ) as span:
854
+ traceparent = span.traceparent()
855
+
856
+ requests.post(
857
+ "https://payments.internal/charge",
858
+ headers={"traceparent": traceparent} if traceparent else {},
859
+ )
860
+
861
+ return {"status": "ok"}
862
+ ```
863
+
864
+ A malformed `traceparent` starts a new trace rather than raising. A missing one (`None`) falls back to the active span, if there is one.
865
+
866
+ A continued trace propagates the sampled flag it was handed, so a downstream sampler sees the decision the head service made. PostHog itself doesn't sample – a span is recorded and exported whichever way that flag is set.
867
+
868
+ ### Linking traces to people and sessions
869
+
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
873
+
874
+ ```python
875
+ from posthog import new_context, identify_context, set_context_session
876
+
877
+ with new_context():
878
+ identify_context(user.id)
879
+ set_context_session(session_id)
880
+
881
+ with posthog.start_span("POST /checkout"):
882
+ process_order()
883
+ ```
884
+
885
+ Spans created outside a context with those values omit the attributes.
886
+
887
+ ### Scrubbing and dropping spans
888
+
889
+ `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
+
891
+ Python
892
+
893
+ ```python
894
+ def scrub_spans(span):
895
+ if span["attributes"].get("http.route") == "/health":
896
+ return None
897
+
898
+ span["attributes"].pop("http.request.header.authorization", None)
899
+ return span
900
+
901
+
902
+ posthog = Posthog(
903
+ "<ph_project_token>",
904
+ host="https://us.i.posthog.com",
905
+ traces={
906
+ "service_name": "checkout-api",
907
+ "before_span_send": scrub_spans,
908
+ },
909
+ )
910
+ ```
911
+
912
+ Attributes are plain Python values, not the OTLP wire encoding. The hook runs after PostHog attaches `posthogDistinctId` and `sessionId`, so those are visible to the hook and can be scrubbed too. An exception's stack trace is on its event, under `event["attributes"]["exception.stacktrace"]`.
913
+
914
+ - `trace_id`, `span_id`, and `parent_span_id` are read-only. Rewriting them would orphan child spans that have already been exported, so changes are reverted.
915
+ - A hook that raises drops the span rather than exporting it without scrubbing.
916
+ - Pass a list to run several hooks in order. The first one to return `None` stops the chain.
917
+ - The hook must be a regular function. An `async` hook drops every span.
918
+ - If an entry isn't callable, tracing turns off for the client rather than exporting spans the hook was meant to scrub.
919
+
920
+ ### Span limits
921
+
922
+ A span is capped at 128 attributes and 128 events, each event at 128 attributes, and each string attribute value at 8192 characters. The endpoint rejects a span that's too large, and a rejected span is lost whole rather than truncated, so the caps bound a span before it gets there.
923
+
924
+ Past the cap, the earliest attributes and events are kept and the number dropped is reported alongside the span, so a truncated span reads as truncated rather than as quietly incomplete. The attributes PostHog attaches itself – `posthogDistinctId` and `sessionId` – don't count toward the cap and are never dropped, so a span at the limit still links back to its person and session.
925
+
926
+ The event cap is absolute: an `exception` event the SDK records for you spends an ordinary slot like any other. A span that fills its events and then raises keeps its `error` status but not the exception detail, and reports the loss as a dropped event. Raise `max_events_per_span` on spans that record many events and can also fail.
927
+
928
+ The length bound reaches inside a value, including strings nested in lists and dictionaries, and applies to `exception.stacktrace` like any other attribute – a long stack trace keeps its last 8192 characters. All four caps are re-applied after `before_span_send`, so a hook that enriches a span can't push it back over.
929
+
930
+ ### Configuration
931
+
932
+ | Option | Default | Description |
933
+ | --- | --- | --- |
934
+ | `service_name` | – | Name of the service producing spans. Set this |
935
+ | `service_version` | – | Version of the service |
936
+ | `environment` | – | Deployment environment, e.g. `production` |
937
+ | `resource_attributes` | – | Extra OTLP resource attributes. Takes precedence over the fields above |
938
+ | `flush_interval` | `5` | Seconds between exports of queued spans |
939
+ | `max_export_batch_size` | `512` | Maximum spans per request |
940
+ | `max_queue_size` | `2048` | Maximum spans held in memory. Spans beyond this are dropped |
941
+ | `max_live_spans` | `10000` | Maximum spans open at once. At the limit `start_span` returns a span that isn't recorded |
942
+ | `max_span_age` | `3600` | Once `max_live_spans` is reached, spans open longer than this many seconds are treated as leaked and never exported |
943
+ | `before_span_send` | – | Edit or drop each finished span before export. Return `None` to drop it |
944
+ | `max_attributes_per_span` | `128` | Maximum attributes you set on one span |
945
+ | `max_events_per_span` | `128` | Maximum events on one span |
946
+ | `max_attribute_value_length` | `8192` | Maximum characters in a string attribute value |
947
+
948
+ An invalid value falls back to its default with a warning. The exception is `before_span_send`: an entry that isn't callable turns tracing off.
949
+
950
+ ### Shutdown and short-lived processes
951
+
952
+ 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
+
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
957
+
958
+ ```python
959
+ def handler(event, context):
960
+ with posthog.start_span("handler"):
961
+ do_work()
962
+ posthog.flush()
963
+ ```
964
+
965
+ 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.
966
+
667
967
  ## GeoIP properties
668
968
 
669
969
  Before posthog-python v3.0, we added GeoIP properties to all incoming events by default. We also used these properties for feature flag evaluation, based on the IP address of the request. This isn't ideal since they are created based on your server IP address, rather than the user's, leading to incorrect location resolution.
@@ -674,8 +974,6 @@ You can go back to previous behavior by doing setting the `disable_geoip` argume
674
974
 
675
975
  Python
676
976
 
677
- PostHog AI
678
-
679
977
  ```python
680
978
  posthog = Posthog('api_key', disable_geoip=False)
681
979
  ```
@@ -694,8 +992,6 @@ You can also explicitly chose to enable or disable GeoIP for a single capture re
694
992
 
695
993
  Python
696
994
 
697
- PostHog AI
698
-
699
995
  ```python
700
996
  posthog.capture('test_event', disable_geoip=True|False)
701
997
  ```
@@ -708,8 +1004,6 @@ You can enable debug mode by setting the `debug` option to `True` in the `PostHo
708
1004
 
709
1005
  Python
710
1006
 
711
- PostHog AI
712
-
713
1007
  ```python
714
1008
  posthog.debug = True
715
1009
  ```
@@ -720,8 +1014,6 @@ You can disable requests during tests by setting the `disabled` option to `True`
720
1014
 
721
1015
  Python
722
1016
 
723
- PostHog AI
724
-
725
1017
  ```python
726
1018
  if settings.TEST:
727
1019
  posthog.disabled = True
@@ -739,10 +1031,9 @@ TCP keepalive probes help prevent idle connections from being dropped by network
739
1031
 
740
1032
  Python
741
1033
 
742
- PostHog AI
743
-
744
1034
  ```python
745
1035
  import posthog
1036
+
746
1037
  posthog.enable_keep_alive()
747
1038
  ```
748
1039
 
@@ -754,10 +1045,9 @@ If you need each request to use a fresh connection, you can disable connection r
754
1045
 
755
1046
  Python
756
1047
 
757
- PostHog AI
758
-
759
1048
  ```python
760
1049
  import posthog
1050
+
761
1051
  posthog.disable_connection_reuse()
762
1052
  ```
763
1053
 
@@ -767,11 +1057,10 @@ For advanced use cases, you can configure arbitrary socket options on the underl
767
1057
 
768
1058
  Python
769
1059
 
770
- PostHog AI
771
-
772
1060
  ```python
773
1061
  import socket
774
1062
  import posthog
1063
+
775
1064
  posthog.set_socket_options([
776
1065
  (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1),
777
1066
  # Add additional socket options as needed
@@ -786,19 +1075,25 @@ Use `before_send` to modify or drop events before they are queued for delivery.
786
1075
 
787
1076
  Python
788
1077
 
789
- PostHog AI
790
-
791
1078
  ```python
792
1079
  from typing import Any
1080
+
793
1081
  import posthog
1082
+
1083
+
794
1084
  def scrub_pii(event: dict[str, Any]) -> dict[str, Any] | None:
795
1085
  properties = event.get("properties", {})
1086
+
796
1087
  if "email" in properties:
797
1088
  email = properties["email"]
798
1089
  properties["email"] = f"***@{email.split('@', 1)[1]}" if "@" in email else "***"
1090
+
799
1091
  if event.get("event") == "test_event":
800
1092
  return None
1093
+
801
1094
  return event
1095
+
1096
+
802
1097
  client = posthog.Client(
803
1098
  "<ph_project_api_key>",
804
1099
  before_send=scrub_pii,
@@ -811,48 +1106,50 @@ If your callback raises an exception, the SDK logs the error and continues with
811
1106
 
812
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.
813
1108
 
814
- PostHog AI
815
-
816
1109
  ### Python
817
1110
 
818
1111
  ```python
819
1112
  from posthog import Posthog
820
1113
  from datetime import datetime
1114
+
821
1115
  posthog = Posthog(
822
1116
  '<ph_project_token>',
823
1117
  host='https://us.i.posthog.com',
824
1118
  debug=True,
825
1119
  historical_migration=True
826
1120
  )
1121
+
827
1122
  events = [
828
1123
  {
829
1124
  "event": "batched_event_name",
830
- "properties": {
831
- "distinct_id": "user_id",
832
- "timestamp": datetime.fromisoformat("2024-04-02T12:00:00")
833
- }
1125
+ "distinct_id": "user_id",
1126
+ "timestamp": datetime.fromisoformat("2024-04-02T12:00:00+00:00"),
1127
+ "properties": {"account_type": "pro"}
834
1128
  },
835
1129
  {
836
1130
  "event": "batched_event_name",
837
- "properties": {
838
- "distinct_id": "used_id",
839
- "timestamp": datetime.fromisoformat("2024-04-02T12:00:00")
840
- }
1131
+ "distinct_id": "user_id",
1132
+ "timestamp": datetime.fromisoformat("2024-04-03T09:30:00+00:00"),
1133
+ "properties": {"account_type": "pro"}
841
1134
  }
842
1135
  ]
1136
+
843
1137
  for event in events:
844
1138
  posthog.capture(
845
- distinct_id=event["properties"]["distinct_id"],
846
- event=event["event"],
1139
+ event["event"],
1140
+ distinct_id=event["distinct_id"],
847
1141
  properties=event["properties"],
848
- timestamp=event["properties"]["timestamp"],
1142
+ timestamp=event["timestamp"],
849
1143
  )
1144
+
1145
+ posthog.shutdown()
850
1146
  ```
851
1147
 
852
1148
  ### Node.js
853
1149
 
854
1150
  ```javascript
855
1151
  import { PostHog } from 'posthog-node'
1152
+
856
1153
  const client = new PostHog(
857
1154
  '<ph_project_token>',
858
1155
  {
@@ -860,28 +1157,40 @@ const client = new PostHog(
860
1157
  historicalMigration: true
861
1158
  }
862
1159
  )
1160
+
863
1161
  client.debug()
1162
+
864
1163
  client.capture({
865
1164
  event: "batched_event_name",
866
1165
  distinctId: "user_id",
867
1166
  properties: {},
868
- timestamp: "2024-04-03T12:00:00Z"
1167
+ timestamp: new Date("2024-04-03T12:00:00Z")
869
1168
  })
1169
+
870
1170
  client.capture({
871
1171
  event: "batched_event_name",
872
1172
  distinctId: "user_id",
873
1173
  properties: {},
874
- timestamp: "2024-04-03T13:00:00Z"
1174
+ timestamp: new Date("2024-04-03T13:00:00Z")
875
1175
  })
1176
+
876
1177
  await client.shutdown()
877
1178
  ```
878
1179
 
879
1180
  ## Serverless environments (Render/Lambda/...)
880
1181
 
881
- By default, the library buffers events before sending them to the capture endpoint, for better performance. This can lead to lost events in serverless environments, if the Python process is terminated by the platform before the buffer is fully flushed. To avoid this, you can either:
1182
+ ### Synchronous `Posthog`
1183
+
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:
1185
+
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.
1188
+
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).
1190
+
1191
+ ### Asyncio `AsyncPosthog`
882
1192
 
883
- - Ensure that `posthog.shutdown()` is called after processing every request by adding a middleware to your server. This allows `posthog.capture()` to remain asynchronous for better performance. `posthog.shutdown()` is blocking.
884
- - Enable the `sync_mode` option when initializing the client, so that all calls to `posthog.capture()` become synchronous.
1193
+ Keep one `AsyncPosthog` client for the lifetime of your application. Use buffered `capture()` by default, or `await capture_immediate()` when one invocation must wait for an event's delivery attempt. Call `await posthog.shutdown()` once during application cleanup. Don't shut down the client after each request.
885
1194
 
886
1195
  ## Django
887
1196