feat(analytics): split og:image tracking by who fetched the image - #10475
Conversation
A social or messenger preview means a human shared a link. That is a product signal and belongs with the human numbers, so those events stay on anyplot.ai. A search or AI crawler fetching the same image is not a share. The distinction only starts mattering now. robots.txt on api.anyplot.ai was a blanket Disallow until #10472, so crawlers were never allowed to fetch /og/ at all — every og_image_view came from a link preview, and recording them all on the main site was right. Opening /og/ changes that: crawler fetches of 3,913 preview images would arrive in volume, drown the sharing signal, and inflate visitor counts on the site whose bot inflation audit 2026-07-08 already had to remove once. The split reuses detect_ai_agent rather than inventing a second rule: if it recognises the agent the fetch is machine-side and goes to bots.anyplot.ai, carrying the same assistant and kind props as bot_fetch so both events slice alike. Anything it does not recognise — Twitter, Facebook, Slack, WhatsApp — is a share and stays put. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR refines server-side Plausible analytics so og_image_view events are recorded on different Plausible sites depending on who fetched the preview image: social/messenger link previews stay on anyplot.ai, while recognized AI/search crawlers are routed to bots.anyplot.ai with assistant/kind properties (reusing detect_ai_agent). This keeps human sharing signals from being diluted now that /og/ is crawlable.
Changes:
- Route
og_image_viewtoBOT_DOMAINwhendetect_ai_agent()recognizes the user agent; includeassistantandkindprops for machine-side events. - Add unit tests asserting the domain/props split for representative share-preview bots vs crawlers.
- Document the new split (and required Plausible property registration) and add a changelog entry.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| api/analytics.py | Splits og_image_view tracking between DOMAIN and BOT_DOMAIN using detect_ai_agent, adding assistant/kind props for machine-side fetches. |
| tests/unit/api/test_analytics.py | Adds async unit tests validating the new audience split and ensuring share-preview user agents remain on the main site without bot props. |
| docs/reference/plausible.md | Documents og_image_view audience split and notes additional property registration needed on bots.anyplot.ai. |
| CHANGELOG.md | Adds an [Unreleased] entry describing the new split behavior and rationale. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…racking-by-audience # Conflicts: # CHANGELOG.md
Same convention fix as #10474, applied to this entry. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The change
anyplot.ai(unchanged)bots.anyplot.aiassistant,kindWhy now, and not before
A preview fetch means a human shared a link — a product signal that belongs with the human numbers. A crawler fetching the same image is not a share.
That distinction was academic until this release.
api.anyplot.ai/robots.txtwas a blanketDisallow: /until #10472, so crawlers were never permitted to fetch/og/at all: everyog_image_viewcame from a link preview, and recording them all on the main site was correct.Opening
/og/changes it. Crawler fetches of 3,913 preview images arrive in volume, and on the main site they would drown the sharing signal and inflate visitor counts — on the very site whose bot inflation an audit already had to remove once (2026-07-08, High #7, ~40% overcount).Implementation
The split reuses
detect_ai_agentrather than inventing a second classification rule. If it recognises the agent, the fetch is machine-side; machine-side events carry the sameassistantandkindprops asbot_fetch, so both events slice identically on the bot dashboard. Anything it does not recognise is treated as a share.Verification
pytest tests/unit— 1638 passed, 7 new: four preview agents asserted to stay on the main site withoutassistantprops, and three crawler classes (search / user-directed / training) asserted to land on the bot site with the right pairruff check+ruff format --check— cleanDashboard follow-up
Machine-side
og_image_viewevents carry the main site's image properties too, sopage,platform,spec,language,libraryand thefilter_*family need registering onbots.anyplot.aias well — noted indocs/reference/plausible.md. Without registration the events land but cannot be broken down.🤖 Generated with Claude Code