<aside> 🔐
Ever wondered how you actually use ChatGPT — what topics you discuss most, how your usage has evolved over time, or how deeply you tend to engage in each session?
You can download your conversation history from ChatGPT and run a simple analysis script to uncover patterns like:
Just export your data from ChatGPT, run a python script over it, and you’ll get a csv file that you can analyze further in tools like Google Sheets, Excel, or back in ChatGPT
The result: a personalized look at how you think, learn, and create with AI — over time.
</aside>
To download your ChatGPT conversation history, click on your profile picture in the bottom-left corner of the ChatGPT browser interface. Then go to Settings → Data Controls → Export Data. ChatGPT will email you a link to download a .zip file containing all your conversations in JSON format. Once you’ve downloaded and unzipped the file, you’ll find a folder that includes conversations.json — that’s the file you’ll use for analysis.
<aside> ✅
Save the conversations.json file in a folder and make a note of the file path. E.g. /Users/yourname/Downloads/chatgpt-export/conversations.json
</aside>
Most people’s ChatGPT conversation history is too large for the app to analyze in a single prompt. Instead, we’ll run a Python script locally — which keeps your data private — to process each conversation, automatically categorize it, and build a clean dataset that’s easy to explore in tools like Google Sheets or Excel.
Below, you'll find a template Python script and a prompt you can paste into an LLM to customize it for your setup—including file paths, categories, and outputs. Paste both in a single prompt and simply replace the placeholders.
<aside> 💬
I need you to customize this ChatGPT conversation analyzer script for my specific use case.
My ChatGPT Export File Location: [REPLACE THIS: Enter the full path to your conversations.json file] Example: /Users/myname/Downloads/conversations.json
Types of Conversations I Have: [REPLACE THIS: Describe what you typically use ChatGPT for - be specific about topics] Example: I'm a software engineer and I use ChatGPT for debugging Python code, learning about web development, writing SQL queries, and occasionally for personal things like recipes and travel planning.
My Preferred Insights: [OPTIONAL - REPLACE THIS: If you have specific category names you want, list them here. Otherwise, the LLM will create appropriate ones based on your description above] Example: Most used word, Work vs. Personal %s, Most active day, Increase in activity over the course of 2025
Please take the attached Python template and:
#!/usr/bin/env python3
"""
ChatGPT Wrapped Analyzer
This script analyzes your ChatGPT usage and generates "Wrapped" style statistics
"""
import json
import pandas as pd
import re
from collections import Counter
from datetime import datetime
import os
# ============================================================================
# CONFIGURATION - TO BE CUSTOMIZED BY LLM BASED ON USER REQUIREMENTS
# ============================================================================
# REPLACE THIS: Insert the file path from user's prompt
JSON_FILE_PATH = "{{JSON_FILE_PATH}}"
# REPLACE THIS: Which year to analyze (2024 or 2025)
ANALYSIS_YEAR = {{YEAR}}
# REPLACE THIS (OPTIONAL): Add custom keywords for "work output" detection
# These help identify conversations where you're creating deliverables vs just exploring
WORK_OUTPUT_KEYWORDS = [
'draft', 'create', 'write', 'build', 'design', 'analyze',
'review', 'edit', 'help', 'feedback', 'improve', 'generate',
'post', 'email', 'deck', 'presentation', 'report', 'document',
'script', 'code', 'analysis', 'strategy', 'make', 'better'
# ADD YOUR OWN: {{CUSTOM_WORK_KEYWORDS}}
]
# REPLACE THIS (OPTIONAL): Add custom keywords for "advice-seeking" conversations
ADVICE_KEYWORDS = [
r'\\bshould i\\b',
r'\\badvice\\b',
r'\\bwhat would you\\b',
r'\\bam i overthinking\\b',
r'\\bam i wrong\\b',
r'\\bis this a good idea\\b',
r'\\bhelp me decide\\b',
r'\\bwhat do you think\\b',
r'\\bdoes this make sense\\b',
r'\\bthoughts\\?\\b',
r'\\bopinion\\b',
r'\\bwhat should\\b'
# ADD YOUR OWN: {{CUSTOM_ADVICE_KEYWORDS}}
]
# ============================================================================
# MAIN SCRIPT - NO CHANGES NEEDED BELOW THIS LINE
# ============================================================================
def load_and_process_json(json_path):
"""Load conversations.json and extract all data"""
print(f"Loading conversations from {json_path}...")
if not os.path.exists(json_path):
raise FileNotFoundError(f"File not found: {json_path}")
with open(json_path, 'r', encoding='utf-8') as f:
conversations = json.load(f)
print(f" → Loaded {len(conversations)} total conversations")
processed = []
for conv in conversations:
conv_data = {
'conversation_id': conv.get('id'),
'title': conv.get('title', 'Untitled'),
'create_time': conv.get('create_time'),
'update_time': conv.get('update_time'),
'user_messages': 0,
'assistant_messages': 0,
'user_text': '',
'assistant_text': ''
}
# Extract messages from mapping
mapping = conv.get('mapping', {})
for node_id, node in mapping.items():
if node.get('message') and node['message'].get('content'):
author = node['message'].get('author', {}).get('role')
parts = node['message']['content'].get('parts', [])
# Extract text from parts
text = ''
for part in parts:
if isinstance(part, str):
text += part + ' '
text = text.strip()
if text and author == 'user':
conv_data['user_messages'] += 1
conv_data['user_text'] += text + ' '
elif text and author == 'assistant':
conv_data['assistant_messages'] += 1
conv_data['assistant_text'] += text + ' '
# Only include conversations with user messages
if conv_data['user_messages'] > 0:
processed.append(conv_data)
return processed
def filter_to_year(conversations, year):
"""Filter conversations to specified year only"""
filtered = []
for conv in conversations:
if conv['create_time']:
try:
dt = datetime.fromtimestamp(conv['create_time'])
if dt.year == year:
conv['datetime'] = dt
filtered.append(conv)
except:
pass
print(f" → Filtered to {year}: {len(filtered)} conversations")
if filtered:
dates = [c['datetime'] for c in filtered]
print(f" → Date range: {min(dates).date()} to {max(dates).date()}")
return filtered
def calculate_basic_stats(conversations):
"""Calculate basic message statistics"""
total_user_messages = sum(c['user_messages'] for c in conversations)
total_conversations = len(conversations)
# Get date range
dates = [c['datetime'].date() for c in conversations]
unique_dates = set(dates)
date_range_days = (max(dates) - min(dates)).days + 1
return {
'total_messages': total_user_messages,
'total_conversations': total_conversations,
'active_days': len(unique_dates),
'messages_per_day': round(total_user_messages / len(unique_dates), 1),
'messages_per_conversation': round(total_user_messages / total_conversations, 1),
'date_range': f"{min(dates).strftime('%B %d, %Y')} to {max(dates).strftime('%B %d, %Y')}"
}
def calculate_politeness(conversations):
"""Count please and thank you"""
all_user_text = ' '.join(c['user_text'].lower() for c in conversations)
please_count = len(re.findall(r'\\bplease\\b', all_user_text))
thank_pattern = r'\\bthank\\b|\\bthanks\\b|\\bthank you\\b'
thank_count = len(re.findall(thank_pattern, all_user_text))
total_messages = sum(c['user_messages'] for c in conversations)
return {
'please_count': please_count,
'thank_count': thank_count,
'total_politeness': please_count + thank_count,
'politeness_per_message': round((please_count + thank_count) / total_messages * 100, 1) if total_messages > 0 else 0
}
def calculate_life_coach_stats(conversations):
"""Detect advice-seeking conversations"""
life_coach_count = 0
for conv in conversations:
text = conv['user_text'].lower()
if any(re.search(pattern, text) for pattern in ADVICE_KEYWORDS):
life_coach_count += 1
return {
'life_coach_conversations': life_coach_count,
'life_coach_percentage': round(life_coach_count / len(conversations) * 100, 1)
}
def calculate_most_used_words(conversations):
"""Find most commonly used words"""
stopwords = {
'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',
'of', 'with', 'by', 'from', 'up', 'about', 'into', 'through', 'during',
'is', 'are', 'was', 'were', 'be', 'have', 'has', 'had', 'do', 'does',
'did', 'will', 'would', 'could', 'should', 'may', 'might', 'can',
'this', 'that', 'these', 'those', 'i', 'you', 'he', 'she', 'it',
'we', 'they', 'what', 'which', 'who', 'when', 'where', 'why', 'how',
'my', 'your', 'me', 'him', 'her', 'us', 'them', 'there', 'their',
'just', 'like', 'make', 'get', 'need', 'want', 'know', 'also',
'chatgpt', 'please', 'thank', 'thanks', 'hi', 'hello', 'hey'
}
all_text = ' '.join(c['user_text'].lower() for c in conversations)
words = re.findall(r'\\b\\w+\\b', all_text)
filtered = [w for w in words if w not in stopwords and len(w) > 3]
word_counts = Counter(filtered).most_common(10)
return {
'top_word': word_counts[0][0] if word_counts else 'N/A',
'top_word_count': word_counts[0][1] if word_counts else 0,
'top_10': word_counts
}
def calculate_refinement_stats(conversations):
"""Calculate refinement and work output stats"""
work_output_count = 0
work_output_iterations = []
for conv in conversations:
is_work = False
# Check title and text for work keywords
title_text = (conv['title'] + ' ' + conv['user_text'][:500]).lower()
if any(keyword in title_text for keyword in WORK_OUTPUT_KEYWORDS):
if conv['user_messages'] >= 3: # Must have refinement
is_work = True
if is_work:
work_output_count += 1
work_output_iterations.append(conv['user_messages'])
avg_work_iterations = sum(work_output_iterations) / len(work_output_iterations) if work_output_iterations else 0
# Overall stats
all_iterations = [c['user_messages'] for c in conversations]
refinement_3plus = sum(1 for i in all_iterations if i >= 3)
refinement_6plus = sum(1 for i in all_iterations if i >= 6)
# Longest conversation
longest = max(conversations, key=lambda c: c['user_messages'])
return {
'avg_iterations_all': round(sum(all_iterations) / len(all_iterations), 1),
'avg_iterations_work': round(avg_work_iterations, 1),
'work_output_count': work_output_count,
'work_output_pct': round(work_output_count / len(conversations) * 100, 1),
'refinement_3plus_pct': round(refinement_3plus / len(conversations) * 100, 1),
'refinement_6plus_pct': round(refinement_6plus / len(conversations) * 100, 1),
'longest_messages': longest['user_messages'],
'longest_title': longest['title']
}
def calculate_time_patterns(conversations):
"""Calculate time-based patterns"""
late_night = sum(1 for c in conversations if c['datetime'].hour >= 23 or c['datetime'].hour <= 5)
weekends = sum(1 for c in conversations if c['datetime'].weekday() >= 5)
# Busiest month
months = [c['datetime'].strftime('%Y-%m') for c in conversations]
month_counts = Counter(months)
busiest_month = month_counts.most_common(1)[0]
# Busiest day of week
days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
weekdays = [days[c['datetime'].weekday()] for c in conversations]
day_counts = Counter(weekdays)
busiest_day = day_counts.most_common(1)[0]
return {
'late_night_count': late_night,
'late_night_pct': round(late_night / len(conversations) * 100, 1),
'weekend_count': weekends,
'weekend_pct': round(weekends / len(conversations) * 100, 1),
'busiest_month': busiest_month[0],
'busiest_month_count': busiest_month[1],
'busiest_day': busiest_day[0],
'busiest_day_count': busiest_day[1]
}
def generate_report(metrics, year):
"""Generate formatted report"""
print("\\n" + "="*80)
print(" "*20 + f"CHATGPT WRAPPED {year} - FULL ANALYSIS")
print("="*80 + "\\n")
print("📊 BASIC STATS")
print("-"*80)
print(f"Date Range: {metrics['basic']['date_range']}")
print(f"Total Messages: {metrics['basic']['total_messages']:,}")
print(f"Total Conversations: {metrics['basic']['total_conversations']:,}")
print(f"Messages Per Day: {metrics['basic']['messages_per_day']}")
print(f"Messages Per Conversation: {metrics['basic']['messages_per_conversation']}")
print()
print("🙏 POLITENESS")
print("-"*80)
print(f"'Please' count: {metrics['politeness']['please_count']}")
print(f"'Thank you' count: {metrics['politeness']['thank_count']}")
print(f"Total politeness: {metrics['politeness']['total_politeness']} ({metrics['politeness']['politeness_per_message']}% of messages)")
print()
print("🔄 REFINEMENT & WORK OUTPUT")
print("-"*80)
print(f"Average iterations (all): {metrics['refinement']['avg_iterations_all']}")
print(f"Work output conversations: {metrics['refinement']['work_output_count']} ({metrics['refinement']['work_output_pct']}%)")
print(f"Average iterations (work): {metrics['refinement']['avg_iterations_work']}")
print(f"3+ iterations: {metrics['refinement']['refinement_3plus_pct']}%")
print(f"6+ iterations: {metrics['refinement']['refinement_6plus_pct']}%")
print(f"Longest: {metrics['refinement']['longest_messages']} messages - '{metrics['refinement']['longest_title']}'")
print()
print("💭 LIFE COACH SESSIONS")
print("-"*80)
print(f"Advice-seeking conversations: {metrics['life_coach']['life_coach_conversations']} ({metrics['life_coach']['life_coach_percentage']}%)")
print()
print("💬 MOST USED WORDS")
print("-"*80)
print(f"Top word: '{metrics['words']['top_word']}' ({metrics['words']['top_word_count']} times)")
print("Top 10:")
for i, (word, count) in enumerate(metrics['words']['top_10'], 1):
print(f" {i}. {word}: {count}")
print()
print("⏰ TIME PATTERNS")
print("-"*80)
print(f"Late night (11pm-5am): {metrics['time']['late_night_count']} ({metrics['time']['late_night_pct']}%)")
print(f"Weekends: {metrics['time']['weekend_count']} ({metrics['time']['weekend_pct']}%)")
print(f"Busiest month: {metrics['time']['busiest_month']} ({metrics['time']['busiest_month_count']} conversations)")
print(f"Busiest day: {metrics['time']['busiest_day']} ({metrics['time']['busiest_day_count']} conversations)")
print()
print("="*80)
def validate_configuration():
"""Validate that all required configuration is properly set"""
errors = []
# Check file path
if JSON_FILE_PATH == "{{JSON_FILE_PATH}}" or not JSON_FILE_PATH:
errors.append("JSON_FILE_PATH must be set to the path of your conversations.json file")
# Check year
if str(ANALYSIS_YEAR) == "{{YEAR}}" or not isinstance(ANALYSIS_YEAR, int):
errors.append("ANALYSIS_YEAR must be set to the year you want to analyze (e.g., 2024 or 2025)")
if errors:
print("\\n❌ Configuration Errors Found:")
print("-" * 40)
for error in errors:
print(f" • {error}")
print("\\nPlease update the configuration section at the top of this script.")
return False
return True
def main():
"""Main execution function"""
print("\\n" + "="*80)
print(" "*25 + "ChatGPT Wrapped Analyzer")
print("="*80)
# Validate configuration
if not validate_configuration():
return
try:
# Load and process
conversations = load_and_process_json(JSON_FILE_PATH)
conversations_filtered = filter_to_year(conversations, ANALYSIS_YEAR)
if not conversations_filtered:
print(f"❌ No {ANALYSIS_YEAR} conversations found!")
return
print("\\nCalculating metrics...")
metrics = {
'basic': calculate_basic_stats(conversations_filtered),
'politeness': calculate_politeness(conversations_filtered),
'refinement': calculate_refinement_stats(conversations_filtered),
'life_coach': calculate_life_coach_stats(conversations_filtered),
'words': calculate_most_used_words(conversations_filtered),
'time': calculate_time_patterns(conversations_filtered)
}
generate_report(metrics, ANALYSIS_YEAR)
# Generate ChatGPT prompt
print("\\n" + "="*80)
print("📋 CHATGPT PROMPT - Copy and paste this into ChatGPT to generate carousel copy:")
print("="*80 + "\\n")
prompt = f"""I have these data points from my ChatGPT usage in {ANALYSIS_YEAR}. Generate 8-10 catchy, viral-worthy carousel slides in the style of Spotify Wrapped/Granola Wrapped. Each slide should have a headline and subtext that's funny, relatable, and impressive.
**Data Points ({ANALYSIS_YEAR} - {metrics['basic']['date_range']}):**
- {metrics['basic']['total_messages']:,} total messages
- {metrics['basic']['messages_per_day']} messages per day
- {metrics['basic']['total_conversations']:,} conversations
- {metrics['basic']['messages_per_conversation']} messages per conversation
- {metrics['refinement']['work_output_pct']}% were work output ({metrics['refinement']['work_output_count']} conversations)
- Work output averaged {metrics['refinement']['avg_iterations_work']} iterations
- {metrics['refinement']['refinement_3plus_pct']}% had 3+ back-and-forths
- Longest: {metrics['refinement']['longest_messages']} messages
- Said 'please' {metrics['politeness']['please_count']} times, 'thank you' {metrics['politeness']['thank_count']} times
- {metrics['life_coach']['life_coach_percentage']}% were advice-seeking
- Most-used word: '{metrics['words']['top_word']}'
- {metrics['time']['late_night_pct']}% after 11pm
- Busiest: {metrics['time']['busiest_day']}s
Create slides that would make people stop scrolling. Mix impressive numbers with humor."""
print(prompt)
print("\\n" + "="*80)
# Save results to output directory
output_dir = os.path.dirname(JSON_FILE_PATH)
output_file = os.path.join(output_dir, f'chatgpt_wrapped_{ANALYSIS_YEAR}_results.txt')
with open(output_file, 'w') as f:
f.write(f"ChatGPT Wrapped {ANALYSIS_YEAR} Results\\n")
f.write("="*80 + "\\n\\n")
f.write(f"Total Messages: {metrics['basic']['total_messages']:,}\\n")
f.write(f"Messages Per Day: {metrics['basic']['messages_per_day']}\\n")
f.write(f"Work Output: {metrics['refinement']['work_output_pct']}%\\n")
f.write(f"Most-Used Word: {metrics['words']['top_word']}\\n")
f.write(f"Politeness: {metrics['politeness']['total_politeness']} please/thank yous\\n")
f.write(f"Busiest Day: {metrics['time']['busiest_day']}\\n\\n")
f.write("Full prompt for ChatGPT:\\n")
f.write("-"*80 + "\\n")
f.write(prompt)
print(f"\\n✅ Results saved to: {output_file}")
except FileNotFoundError as e:
print(f"\\n❌ Error: {e}")
print("Please check that the JSON file path is correct.")
except json.JSONDecodeError as e:
print(f"\\n❌ Error parsing JSON file: {e}")
print("Please ensure the file is a valid ChatGPT export.")
except Exception as e:
print(f"\\n❌ Unexpected error: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
main()
Before we run the script that we created, make sure you have Python 3 and the required dependencies installed.
Check if Python 3 is installed
Open your terminal (or iTerm / Warp) and run:
python3 --version
You should see something like Python 3.9.6 or higher.
If you get “command not found,” download it from python.org/downloads and then re-run the command.
Install the dependencies
Once Python is ready, install the required libraries:
python3 -m pip install pandas
(This also installs numpy, which pandas depends on.)
You only need to do this once — after that, you can run the analyzer script anytime.
Follow these steps to run your ChatGPT analysis on your own computer: