How to Automate Microsoft Clarity Reports with AI
Most teams install Clarity, check the dashboard once, and then forget about it. The data keeps flowing, but nobody looks at it. Here's how to build an automated system that collects Clarity data, feeds it to an LLM, and sends you a weekly email with actual, actionable insights.
Why Manual Clarity Reporting Wastes Time
The typical Clarity workflow looks like this: someone on the team logs into the dashboard every few weeks, scrolls through heatmaps and session recordings for 30 minutes, and walks away with a vague sense that "things seem okay." No notes. No comparisons. No action items.
This approach has three problems:
- Inconsistency: Without a regular schedule, you miss regressions until they become obvious in conversion data
- No historical context: The dashboard shows the current state, not trends over time
- Analysis paralysis: Heatmaps and recordings are overwhelming without a framework for prioritization
Automated reporting solves all three. Data is collected every day, analyzed every week, and delivered with specific recommendations -- no dashboard login required.
Architecture Overview
The automated pipeline has four components:
Daily (cron): Clarity API --> SQLite/Postgres
Weekly (cron): SQLite --> LLM Analysis --> Email Report
Components:
1. collect.py - Fetches daily metrics from Clarity API
2. clarity.db - Stores per-page metrics over time
3. report.py - Queries 7 days, sends to LLM, emails the report
4. cron - Schedules both scripts
Step 1: Daily Data Collection
The collection script runs once daily and stores that day's metrics. This is the foundation -- without historical data, the AI has nothing to compare against.
import requests
import sqlite3
import os
from datetime import date
def collect():
# Initialize database
conn = sqlite3.connect("clarity.db")
conn.execute("""
CREATE TABLE IF NOT EXISTS metrics (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT NOT NULL,
page_url TEXT,
sessions INTEGER,
users INTEGER,
scroll_depth REAL,
active_time REAL,
dead_clicks INTEGER,
rage_clicks INTEGER,
quick_backs INTEGER,
excessive_scrolls INTEGER,
UNIQUE(date, page_url)
)
""")
# Fetch from Clarity API
response = requests.post(
"https://www.clarity.ms/export-data/api/v1/project-live-insights",
headers={
"Authorization": f"Bearer {os.environ['CLARITY_API_TOKEN']}",
"Content-Type": "application/json"
},
json={
"projectId": os.environ["CLARITY_PROJECT_ID"],
"numOfDays": 1
}
)
response.raise_for_status()
results = response.json().get("results", [])
# Store each page's metrics
today = date.today().isoformat()
for row in results:
conn.execute("""
INSERT OR IGNORE INTO metrics
(date, page_url, sessions, users, scroll_depth,
active_time, dead_clicks, rage_clicks,
quick_backs, excessive_scrolls)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
today, row.get("url"), row.get("totalSessionCount"),
row.get("distinctUserCount"), row.get("scrollDepth"),
row.get("activeTime"), row.get("deadClickCount"),
row.get("rageClickCount"), row.get("quickBackCount"),
row.get("excessiveScrollCount")
))
conn.commit()
print(f"Collected {len(results)} pages for {today}")
if __name__ == "__main__":
collect()
Tip: Use INSERT OR IGNORE with a unique constraint on (date, page_url). This makes the script idempotent -- running it twice in one day won't create duplicates.
Step 2: Weekly Report Generation
The report script queries 7 days of stored data, formats it into a structured prompt, sends it to an LLM, and emails the result.
Query the Data
import sqlite3
from datetime import date, timedelta
def get_weekly_data():
conn = sqlite3.connect("clarity.db")
week_ago = (date.today() - timedelta(days=7)).isoformat()
# Per-page summary for the week
rows = conn.execute("""
SELECT
page_url,
SUM(sessions) as total_sessions,
AVG(scroll_depth) as avg_scroll,
SUM(dead_clicks) as dead_clicks,
SUM(rage_clicks) as rage_clicks,
SUM(quick_backs) as quick_backs,
AVG(active_time) as avg_active_time
FROM metrics
WHERE date >= ?
GROUP BY page_url
ORDER BY total_sessions DESC
LIMIT 20
""", (week_ago,)).fetchall()
return rows
Analyze with an LLM
The key to useful AI analysis is a well-structured prompt. Don't just dump numbers -- provide context about what each metric means and what you want the AI to look for.
from openai import OpenAI
def analyze_with_ai(data_rows):
client = OpenAI()
# Format data as a readable table
data_text = "Page | Sessions | Scroll% | Dead Clicks | Rage Clicks | Quick Backs | Active Time\n"
data_text += "-" * 90 + "\n"
for row in data_rows:
data_text += f"{row[0]} | {row[1]} | {row[2]:.0f}% | {row[3]} | {row[4]} | {row[5]} | {row[6]:.1f}s\n"
prompt = f"""Analyze this week's Microsoft Clarity data for a website.
Focus on:
1. Pages with high frustration signals (rage clicks, dead clicks, quick-backs)
2. Pages with unusually low scroll depth (users not engaging)
3. Week-over-week changes if visible
4. Specific, actionable recommendations (not generic advice)
Data:
{data_text}
Respond with:
- Executive Summary (2-3 sentences)
- Top Issues (ranked by impact)
- Recommendations (specific actions with expected impact)
- Pages Performing Well (positive signals)"""
response = client.chat.completions.create(
model="gpt-4.1-mini",
messages=[
{"role": "system", "content": "You are a UX analyst specializing in web analytics. Be specific and actionable. Avoid generic advice."},
{"role": "user", "content": prompt}
],
temperature=0.3
)
return response.choices[0].message.content
Info: Use a low temperature (0.2-0.4) for analytical tasks. This keeps the LLM focused on the data rather than generating creative but unfounded interpretations. GPT-4.1-mini works well for this -- you don't need the full GPT-4 for structured data analysis.
Send the Email
Use any transactional email service. Here's an example with Resend:
import requests
import os
def send_report(analysis_text, data_rows):
# Convert analysis to HTML
html_body = f"""
<html>
<body style="font-family: sans-serif; max-width: 600px; margin: 0 auto;">
<h1>Weekly Clarity Report</h1>
<pre style="white-space: pre-wrap;">{analysis_text}</pre>
<hr>
<p style="color: #666; font-size: 12px;">
Generated automatically from {len(data_rows)} pages of Clarity data.
</p>
</body>
</html>
"""
requests.post(
"https://api.resend.com/emails",
headers={
"Authorization": f"Bearer {os.environ['RESEND_API_KEY']}",
"Content-Type": "application/json"
},
json={
"from": "reports@yourdomain.com",
"to": os.environ["REPORT_EMAIL"],
"subject": f"Clarity Weekly Report - {date.today().isoformat()}",
"html": html_body
}
)
Step 3: Schedule with Cron
Two cron entries handle the entire pipeline:
# Collect data daily at 7 AM
0 7 * * * cd /path/to/project && source .env && python collect.py >> logs/collect.log 2>&1
# Generate and send weekly report every Monday at 8 AM
0 8 * * 1 cd /path/to/project && source .env && python report.py >> logs/report.log 2>&1
Warning: Always redirect cron output to a log file. Silent failures are the most common reason automated pipelines stop working. Check your logs weekly, or better yet, add error alerting.
Making the AI Analysis Actually Useful
The difference between a useless AI report and a valuable one is entirely in the prompt. Here are the patterns that work:
Include Context
Tell the LLM what your site does, what your key conversion pages are, and what you changed recently. "Rage clicks on /checkout increased 40%" is more useful when the AI knows you just redesigned the checkout flow.
Request Specific Formats
Ask for prioritized lists, not paragraphs. "Rank issues by estimated impact on conversions" gives you a clear action list. "Analyze the data" gives you a wall of text.
Provide Benchmarks
If you have previous weeks' data, include it in the prompt. "Last week's rage clicks on /pricing: 45. This week: 120" lets the AI flag the 167% increase rather than just reporting the current number.
DIY vs. ClarityInsights
Building this pipeline yourself is entirely feasible -- the code above is production-ready with minor modifications. But there are trade-offs:
| Aspect | DIY Pipeline | ClarityInsights |
|---|---|---|
| Setup time | 2-4 hours | 5 minutes |
| Maintenance | You handle API changes, errors, server uptime | Managed |
| AI prompt quality | Depends on your prompt engineering | Tuned for Clarity data specifically |
| Week-over-week comparison | Build it yourself | Built-in |
| Multi-project | Duplicate scripts per project | Add projects in dashboard |
| Cost | Server + OpenAI API (~$2/month) | Subscription |
| Customization | Full control | Predefined report format |
If you're a developer comfortable with Python and cron, the DIY route works well for a single project. If you manage multiple sites, or if you want polished reports without maintaining infrastructure, ClarityInsights handles the entire pipeline.
Going Further
Once the basic pipeline is running, consider these enhancements:
- Slack alerts: Send immediate alerts when rage clicks exceed a threshold instead of waiting for the weekly report
- Trend charts: Generate matplotlib charts and embed them in the email for visual trend analysis
- Page-group analysis: Group URLs by pattern (
/blog/*,/product/*) for section-level insights - Anomaly detection: Use z-scores to automatically flag metrics that deviate significantly from their historical average
- Correlation with deploys: Tag your database with deploy dates and let the AI compare before/after metrics
Stop analyzing Clarity data manually
ClarityInsights sends you AI-powered weekly reports with per-page analysis, frustration signals, and prioritized recommendations.
Join the Waitlist