Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Documentation usage analytics and insights. Integrate with Google Analytics, Algolia analytics, and custom tracking to measure documentation effectiveness, identify content gaps, and optimize user journeys.
.claude/skills/a5c-ai-docs-analytics/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 46% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 195% | 0% |
| case-16 | ✗→✓ | ▲ Improved | 217% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 179% | 0% |
| case-05 | ✓→✗ | ▼ Worse | 212% | 0% |
Measure documentation effectiveness with analytics integration, search insights, user journey analysis, and content performance metrics.
Invoke this skill when you need to:
| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | docsUrl | string | Yes | Documentation site URL | | analyticsProvider | string | No | ga4, algolia, plausible, custom | | trackingId | string | No | Analytics tracking ID | | algoliaAppId | string | No | Algolia application ID | | algoliaApiKey | string | No | Algolia API key for analytics | | enableHeatmaps | boolean | No | Enable heatmap tracking | | customEvents | array | No | Custom events to track |
json{ "docsUrl": "https://docs.example.com", "analyticsProvider": "ga4", "trackingId": "G-XXXXXXXXXX", "algoliaAppId": "ALGOLIA_APP_ID", "enableHeatmaps": true, "customEvents": [ "code_copy", "feedback_submitted", "version_switch" ] }
analytics/
├── reports/
│ ├── monthly-summary.json
│ ├── search-analysis.json
│ ├── content-gaps.json
│ └── user-journeys.json
├── dashboards/
│ ├── overview.html
│ └── search-insights.html
└── config/
├── ga4-config.json
└── algolia-config.jsonjavascript// analytics.js window.dataLayer = window.dataLayer || []; function gtag() { dataLayer.push(arguments); } gtag('js', new Date()); gtag('config', 'G-XXXXXXXXXX', { // Custom dimensions for docs custom_map: { dimension1: 'doc_version', dimension2: 'doc_section', dimension3: 'search_query', dimension4: 'code_language', }, }); // Track documentation version gtag('set', 'user_properties', { doc_version: document.querySelector('meta[name="docs-version"]')?.content, });
javascript// Track code block copy document.querySelectorAll('pre code').forEach((block) => { block.addEventListener('click', () => { gtag('event', 'code_copy', { event_category: 'engagement', event_label: block.className, // language page_location: window.location.href, }); }); }); // Track documentation feedback function trackFeedback(helpful, pageUrl) { gtag('event', 'doc_feedback', { event_category: 'feedback', event_label: helpful ? 'helpful' : 'not_helpful', page_location: pageUrl, }); } // Track version switching function trackVersionSwitch(fromVersion, toVersion) { gtag('event', 'version_switch', { event_category: 'navigation', from_version: fromVersion, to_version: toVersion, }); } // Track time on page let startTime = Date.now(); window.addEventListener('beforeunload', () => { const timeSpent = Math.round((Date.now() - startTime) / 1000); gtag('event', 'time_on_page', { event_category: 'engagement', value: timeSpent, page_location: window.location.href, }); }); // Track scroll depth let maxScroll = 0; window.addEventListener('scroll', () => { const scrollPercent = Math.round( (window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100 ); if (scrollPercent > maxScroll) { maxScroll = scrollPercent; if ([25, 50, 75, 90, 100].includes(scrollPercent)) { gtag('event', 'scroll_depth', { event_category: 'engagement', value: scrollPercent, page_location: window.location.href, }); } } }); // Track external link clicks document.querySelectorAll('a[href^="http"]').forEach((link) => { link.addEventListener('click', () => { gtag('event', 'outbound_click', { event_category: 'engagement', event_label: link.href, page_location: window.location.href, }); }); });
javascript// Algolia DocSearch with analytics import docsearch from '@docsearch/js'; docsearch({ appId: 'YOUR_APP_ID', apiKey: 'YOUR_SEARCH_API_KEY', indexName: 'YOUR_INDEX_NAME', container: '#docsearch', debug: false, insights: true, // Enable Algolia analytics searchParameters: { analytics: true, clickAnalytics: true, enablePersonalization: false, }, });
javascript// Fetch search analytics from Algolia const algoliasearch = require('algoliasearch'); const analyticsClient = algoliasearch('APP_ID', 'ADMIN_API_KEY'); async function getSearchAnalytics() { const index = analyticsClient.initIndex('docs'); // Get top searches const topSearches = await analyticsClient.customRequest({ method: 'GET', path: '/2/searches', data: { index: 'docs', startDate: '2026-01-01', endDate: '2026-01-24', limit: 100, orderBy: 'searchCount', }, }); // Get searches with no results const noResultSearches = await analyticsClient.customRequest({ method: 'GET', path: '/2/searches/noResults', data: { index: 'docs', startDate: '2026-01-01', endDate: '2026-01-24', limit: 100, }, }); // Get click-through rate const clickAnalytics = await analyticsClient.customRequest({ method: 'GET', path: '/2/clicks/clickThroughRate', data: { index: 'docs', startDate: '2026-01-01', endDate: '2026-01-24', }, }); return { topSearches: topSearches.searches, noResultSearches: noResultSearches.searches, clickThroughRate: clickAnalytics, }; }
javascript// Analyze search queries that return no results async function analyzeContentGaps(noResultSearches) { const gaps = []; for (const search of noResultSearches) { // Categorize by topic const category = categorizeQuery(search.search); gaps.push({ query: search.search, count: search.count, category, suggestedContent: generateContentSuggestion(search.search), priority: calculatePriority(search.count), }); } return gaps.sort((a, b) => b.count - a.count); } function categorizeQuery(query) { const categories = { api: /api|endpoint|rest|graphql|webhook/i, authentication: /auth|login|oauth|token|api.?key/i, integration: /integrate|connect|setup|install/i, error: /error|fail|issue|problem|not.?work/i, pricing: /price|cost|plan|billing/i, }; for (const [category, pattern] of Object.entries(categories)) { if (pattern.test(query)) return category; } return 'general'; }
javascript// Track user journey through documentation const journey = { sessionId: generateSessionId(), startTime: Date.now(), pages: [], searches: [], events: [], }; // Track page views function trackPageView(pageUrl, pageTitle) { journey.pages.push({ url: pageUrl, title: pageTitle, timestamp: Date.now(), timeOnPrevPage: calculateTimeOnPrevPage(), }); } // Track searches function trackSearch(query, results) { journey.searches.push({ query, resultsCount: results.length, timestamp: Date.now(), clickedResult: null, }); } // Track search result click function trackSearchClick(query, resultUrl, position) { const search = journey.searches.find((s) => s.query === query); if (search) { search.clickedResult = { url: resultUrl, position }; } } // Analyze journey patterns function analyzeJourney(journey) { return { totalPages: journey.pages.length, totalTime: Date.now() - journey.startTime, searchesBeforeSuccess: countSearchesBeforeSuccess(journey), commonPaths: identifyCommonPaths(journey.pages), dropOffPoints: identifyDropOffPoints(journey.pages), }; }
javascript// Identify common documentation paths async function getCommonPaths(journeys) { const pathCounts = {}; journeys.forEach((journey) => { const path = journey.pages .map((p) => p.url) .slice(0, 5) .join(' -> '); pathCounts[path] = (pathCounts[path] || 0) + 1; }); return Object.entries(pathCounts) .sort((a, b) => b[1] - a[1]) .slice(0, 20) .map(([path, count]) => ({ path, count, percentage: ((count / journeys.length) * 100).toFixed(1), })); }
javascript// Calculate content engagement score function calculateEngagementScore(pageMetrics) { const weights = { avgTimeOnPage: 0.3, scrollDepth: 0.2, codeBlockInteractions: 0.2, feedbackScore: 0.15, exitRate: -0.15, // Negative weight }; return Object.entries(weights).reduce((score, [metric, weight]) => { return score + normalizeMetric(pageMetrics[metric]) * weight; }, 0); } // Page performance report function generatePageReport(pageUrl) { return { url: pageUrl, metrics: { pageviews: getPageviews(pageUrl), uniqueVisitors: getUniqueVisitors(pageUrl), avgTimeOnPage: getAvgTimeOnPage(pageUrl), bounceRate: getBounceRate(pageUrl), exitRate: getExitRate(pageUrl), scrollDepth: { '25%': getScrollDepthPercent(pageUrl, 25), '50%': getScrollDepthPercent(pageUrl, 50), '75%': getScrollDepthPercent(pageUrl, 75), '100%': getScrollDepthPercent(pageUrl, 100), }, feedback: { helpful: getHelpfulCount(pageUrl), notHelpful: getNotHelpfulCount(pageUrl), score: getFeedbackScore(pageUrl), }, codeInteractions: getCodeInteractions(pageUrl), }, engagementScore: calculateEngagementScore(pageMetrics), recommendations: generateRecommendations(pageMetrics), }; }
json{ "period": "2026-01", "summary": { "totalSearches": 45230, "uniqueSearches": 8432, "noResultSearches": 1234, "avgClickThroughRate": 0.68 }, "contentGaps": [ { "query": "webhook authentication", "searchCount": 342, "category": "authentication", "suggestedContent": { "type": "guide", "title": "Webhook Authentication Guide", "outline": [ "Introduction to webhook security", "Signature verification", "Best practices" ] }, "priority": "high" }, { "query": "rate limiting best practices", "searchCount": 256, "category": "api", "suggestedContent": { "type": "guide", "title": "Rate Limiting Best Practices", "outline": [ "Understanding rate limits", "Handling 429 responses", "Exponential backoff implementation" ] }, "priority": "high" } ], "topSearches": [ { "query": "authentication", "count": 1543 }, { "query": "api keys", "count": 1232 }, { "query": "getting started", "count": 987 } ], "lowPerformingPages": [ { "url": "/docs/advanced/caching", "issues": ["high bounce rate", "low scroll depth"], "recommendations": [ "Add more code examples", "Include visual diagrams" ] } ] }
javascript// docusaurus.config.js module.exports = { plugins: [ [ '@docusaurus/plugin-google-gtag', { trackingID: 'G-XXXXXXXXXX', anonymizeIP: true, }, ], ], themeConfig: { algolia: { appId: 'YOUR_APP_ID', apiKey: 'YOUR_SEARCH_API_KEY', indexName: 'YOUR_INDEX_NAME', insights: true, }, }, scripts: [ { src: '/js/custom-analytics.js', async: true, }, ], };
yaml# mkdocs.yml plugins: - search: analytics: provider: algolia property: YOUR_INDEX_NAME extra: analytics: provider: google property: G-XXXXXXXXXX feedback: title: Was this page helpful? ratings: - icon: material/emoticon-happy-outline name: This page was helpful data: 1 note: Thanks for your feedback! - icon: material/emoticon-sad-outline name: This page could be improved data: 0 note: Thanks! Help us improve by using the feedback form.
json{ "dependencies": { "algoliasearch": "^4.0.0", "@docsearch/js": "^3.0.0" }, "devDependencies": { "@google-analytics/data": "^4.0.0" } }
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 31,604 | 29,127 | -8% | 1 | 1 | 0% | 6,773 | 9,916 | +46% | 0 | 0 | — |
case-02 | fail→fail | 27,463 | 23,746 | -14% | 1 | 1 | 0% | 4,170 | 7,957 | +91% | 0 | 0 | — |
case-03 | fail→fail | 59,591 | 46,189 | -22% | 1 | 1 | 0% | 8,265 | 12,370 | +50% | 0 | 0 | — |
case-04 | pass→pass | 14,571 | 15,660 | +7% | 1 | 1 | 0% | 2,050 | 6,351 | +210% | 0 | 0 | — |
case-05 | pass→fail | 20,034 | 36,406 | +82% | 1 | 1 | 0% | 2,226 | 6,940 | +212% | 0 | 0 | — |
case-06 | pass→pass | 30,511 | 33,047 | +8% | 1 | 1 | 0% | 3,642 | 8,683 | +138% | 0 | 0 | — |
case-07 | pass→pass | 17,915 | 18,954 | +6% | 1 | 1 | 0% | 2,546 | 6,272 | +146% | 0 | 0 | — |
case-08 | fail→fail | 18,728 | 20,981 | +12% | 1 | 1 | 0% | 2,687 | 7,213 | +168% | 0 | 0 | — |
case-09 | pass→pass | 8,017 | 10,973 | +37% | 1 | 1 | 0% | 1,290 | 5,236 | +306% | 0 | 0 | — |
case-10 | pass→pass | 8,858 | 11,876 | +34% | 1 | 1 | 0% | 1,607 | 5,372 | +234% | 0 | 0 | — |
case-11 | pass→pass | 15,301 | 13,586 | -11% | 1 | 1 | 0% | 2,271 | 5,640 | +148% | 0 | 0 | — |
case-12 | pass→pass | 16,579 | 18,075 | +9% | 1 | 1 | 0% | 3,194 | 7,911 | +148% | 0 | 0 | — |
case-13 | fail→fail | 20,359 | 32,351 | +59% | 1 | 1 | 0% | 2,915 | 9,867 | +238% | 0 | 0 | — |
case-14 | fail→fail | 15,127 | 20,527 | +36% | 1 | 1 | 0% | 2,974 | 7,125 | +140% | 0 | 0 | — |
case-15 | fail→pass | 17,100 | 19,575 | +14% | 1 | 1 | 0% | 2,289 | 6,758 | +195% | 0 | 0 | — |
case-16 | fail→pass | 14,047 | 7,321 | -48% | 1 | 1 | 0% | 1,767 | 5,596 | +217% | 0 | 0 | — |
case-17 | fail→fail | 17,657 | 10,853 | -39% | 1 | 1 | 0% | 2,155 | 4,435 | +106% | 0 | 0 | — |
case-18 | fail→fail | 15,814 | 22,842 | +44% | 1 | 1 | 0% | 1,938 | 7,471 | +286% | 0 | 0 | — |
case-19 | fail→pass | 16,026 | 9,120 | -43% | 1 | 1 | 0% | 2,055 | 5,725 | +179% | 0 | 0 | — |
case-20 | pass→pass | 18,253 | 22,261 | +22% | 1 | 1 | 0% | 2,640 | 6,903 | +161% | 0 | 0 | — |
case-21 | fail→fail | 12,566 | 12,307 | -2% | 1 | 1 | 0% | 2,171 | 5,303 | +144% | 0 | 0 | — |
case-22 | fail→fail | 21,618 | 12,494 | -42% | 1 | 1 | 0% | 3,097 | 6,547 | +111% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 21 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +14 percentage points is the difference between those two pass rates over the 21 comparable cases. 2 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.