DocsRecipes

Recipes

Content and documentation analytics

4 min readReviewed July 2026

Content analytics should explain whether people find the right material, consume enough of it to make progress, and complete the task the content was designed to support. Page views alone cannot answer those questions.

This recipe applies to documentation, blogs, guides, changelogs, learning centers, and in-product help. It combines Web analytics, Product search analytics, and Survey and feedback analytics.

Build a content catalog#

Give each published item stable metadata:

{
  "content_id": "docs:reliable_event_delivery",
  "content_version": 7,
  "content_type": "documentation",
  "section": "guides",
  "topic": "event_delivery",
  "audience": "backend_engineer",
  "owner_team": "developer_experience",
  "published_at": "2026-07-26T00:00:00Z",
  "last_reviewed_month": "2026-07",
  "status": "published"
}

Keep titles and full text in the content system. Send bounded identifiers and categories to GraphJSON.

Stable content IDs let a page move without breaking its history. Increment content_version when meaning changes materially, not for every typo.

Instrument content views#

{
  "event": "content_viewed",
  "view_id": "view_01J...",
  "session_id": "sess_42",
  "content_id": "docs:reliable_event_delivery",
  "content_version": 7,
  "content_type": "documentation",
  "section": "guides",
  "topic": "event_delivery",
  "route_template": "/docs/Guides/:slug",
  "entry_method": "internal_search",
  "referrer_type": "same_site",
  "release": "web-2026.07.26.1"
}

Generate view_id for the rendered content instance. Exclude full referrer URLs, query strings, and page paths containing user input.

Distinguish a server request, client render, and view that became visible. Automated previews and prefetches are not human views.

Measure engaged reading carefully#

Time on page is not automatically reading. A tab can remain open in the background.

Use:

  • page visible and focused
  • periodic bounded heartbeats
  • content scroll progress
  • interaction with examples or navigation
  • terminal page-hide or route-change event

Emit one derived terminal summary:

{
  "event": "content_engagement_completed",
  "view_id": "view_01J...",
  "session_id": "sess_42",
  "content_id": "docs:reliable_event_delivery",
  "content_version": 7,
  "engaged_seconds": 184,
  "maximum_progress_bucket": "75_99",
  "example_interactions": 2,
  "outcome": "continued_to_related_guide",
  "engagement_definition_version": 3
}

Use progress buckets rather than precise scroll percentages. Long pages and short pages are not directly comparable.

Do not claim that engaged time proves comprehension. A confusing article can produce long sessions.

Track high-intent content actions#

Useful controlled events:

  • code example copied
  • API request example expanded
  • related guide opened
  • external reference followed
  • download started
  • changelog entry opened
  • edit or issue link selected
  • CTA selected
{
  "event": "content_action_completed",
  "view_id": "view_01J...",
  "content_id": "docs:reliable_event_delivery",
  "action": "code_example_copied",
  "example_key": "typescript_outbox_worker",
  "destination_type": "clipboard"
}

Do not send copied text or arbitrary link URLs.

Preserve one search journey:

search request
  → results returned
  → content selected
  → content engagement
  → next task or product outcome

Attach the originating search_id and selected rank to the content view. This supports:

  • zero-result topics
  • selected-result rank
  • reformulation after reading
  • search exits
  • downstream success

Do not log raw queries by default. Use controlled topic classes and privacy-safe diagnostics from the search guide.

Define content success by intent#

Different content types need different outcomes:

Content Useful outcome
Quickstart First accepted event
API reference Successful request or resolved integration error
Troubleshooting Problem resolved without repeated search or support contact
Concept guide Related implementation guide started
Changelog Affected migration or review action completed
Blog article Qualified continuation, signup, or later product activation

Avoid one universal content-conversion metric.

When permitted, carry a content_view_id or session-level content touch to a later product fact:

{
  "event": "first_event_accepted",
  "account_id": "acct_7",
  "user_id": "usr_42",
  "content_view_id": "view_01J...",
  "assistance_type": "quickstart",
  "result": "success"
}

Define the attribution window and competing touches. Call the result content-assisted, not content-caused.

Anonymous-to-known identity must follow the documented consent and identity-stitching policy.

Query content-assisted outcomes#

WITH views AS (
  SELECT
    JSONExtractString(json, 'view_id') AS view_id,
    argMin(JSONExtractString(json, 'content_id'), timestamp) AS content_id,
    argMin(JSONExtractString(json, 'session_id'), timestamp) AS session_id,
    min(toDateTime(timestamp)) AS viewed_at
  FROM content_events
  WHERE JSONExtractString(json, 'event') = 'content_viewed'
  GROUP BY view_id
),
outcomes AS (
  SELECT
    JSONExtractString(json, 'session_id') AS session_id,
    min(toDateTime(timestamp)) AS outcome_at
  FROM product_events
  WHERE
    JSONExtractString(json, 'event') = 'first_event_accepted'
    AND JSONExtractString(json, 'result') = 'success'
  GROUP BY session_id
)
SELECT
  views.content_id,
  uniqExact(views.session_id) AS mature_view_sessions,
  uniqExactIf(
    views.session_id,
    outcomes.outcome_at >= views.viewed_at
    AND outcomes.outcome_at < views.viewed_at + INTERVAL 7 DAY
  ) AS assisted_sessions,
  assisted_sessions / nullIf(mature_view_sessions, 0) AS assisted_rate
FROM views
LEFT JOIN outcomes USING session_id
WHERE views.viewed_at < now() - INTERVAL 7 DAY
GROUP BY views.content_id
ORDER BY mature_view_sessions DESC

One session can view several pages before the outcome. This query gives each page assistance credit; it is not a single-touch causal attribution model.

Collect feedback with context#

{
  "event": "content_feedback_submitted",
  "feedback_id": "fb_81",
  "view_id": "view_01J...",
  "content_id": "docs:reliable_event_delivery",
  "content_version": 7,
  "response": "not_helpful",
  "reason_category": "missing_example",
  "surface": "page_footer"
}

Use controlled reason categories. Keep free-text comments in a dedicated, access-controlled feedback system.

Show feedback response rate. A small self-selected group is not representative of all readers.

Measure content paths#

Useful journeys:

  • landing page → concept → quickstart → first event
  • error search → troubleshooting → API reference → successful request
  • blog → docs → signup → activation
  • changelog → migration guide → implementation completion

Remove global navigation and repeated refresh noise. Use content_id, not page title, in path arrays.

Do not assume the last page was the only helpful page.

Find stale and missing content#

Join catalog metadata with behavior:

  • high traffic with old review date
  • high search exits
  • repeated reformulation after a page
  • low example interaction on implementation pages
  • increased negative feedback after a product change
  • support categories with no corresponding guide
  • broken or removed destination actions

Traffic is not the only priority. Low-traffic security, billing, recovery, and incident documentation can still be critical.

Use the Event catalog pattern for ownership and lifecycle.

Filter invalid traffic#

Exclude or separate:

  • search-engine crawlers
  • link unfurlers
  • uptime checks
  • accessibility and visual regression bots
  • documentation build previews
  • employees and test accounts
  • repeated reloads from development

Use server-verified flags and maintained reference data. User-agent filtering alone is incomplete.

Build the content dashboard#

Show:

  1. eligible human views by content type and topic
  2. entry source and internal-search contribution
  3. engaged-time and progress distributions
  4. high-intent content actions
  5. search reformulation and exit after content
  6. intent-specific assisted outcomes
  7. feedback rate and controlled reasons
  8. common learning paths
  9. high-impact stale or missing content
  10. bot-filter and instrumentation coverage

Keep content version and review month available during investigations.

Launch checklist#

  • Every item has a stable content ID, owner, type, topic, and version.
  • Views represent rendered visible content, not server requests.
  • Full URLs, raw searches, copied text, and feedback text are excluded.
  • Engaged time stops for background or hidden pages.
  • Progress uses bounded buckets and page-length context.
  • Content outcomes reflect the item’s intended job.
  • Assisted outcomes use explicit windows and causal language.
  • Search, feedback, and product identities follow consent policy.
  • Bots, previews, and internal traffic are separated.
  • Staleness review includes critical low-traffic content.
Need a hand?

Tell us what you’re building and we’ll point you in the right direction.

Contact support