Transform your exported ChatGPT conversations into beautifully formatted Markdown files optimized for Obsidian and other markdown readers.
Two ways to use this tool:
- 🌐 Browser-Based Converter (Recommended) - Easy, no installation required
- 💻 Python Script - For terminal users who prefer command-line tools
Both handle the complete ChatGPT export including conversations, images, DALL-E generations, and all attachments.
📥 Download chatgpt-markdown-converter.html (Right-click → Save As)
Then:
- Open the downloaded HTML file in your browser (Chrome, Firefox, Safari, Edge - all work!)
- Upload your ChatGPT export ZIP file
- Configure your preferences (name, organization mode, formatting)
- Convert - all processing happens in your browser (nothing uploaded!)
- Download your organized markdown files
That's it! No Python, no terminal, no dependencies. Everything runs locally in your browser.
💡 Privacy First: All processing happens in your browser. Your conversations never leave your computer.
⚠️ File Size Limit: The browser converter supports ZIP files up to 2 GB. For larger exports, use the Python CLI script instead.
- Complete ChatGPT Export Processing - Handles the entire ChatGPT data export including all files and folders
- Multimodal Content Support - Automatically extracts and embeds images, screenshots, and attachments
- DALL-E Image Organization - Separate handling for AI-generated images
- Smart File Organization - Creates conversation-specific folders for all attachments
- Regular Messages - Standard user and assistant conversations
- Audio Messages - Voice conversations embedded with HTML5 audio players
- Internal Reasoning - ChatGPT's thinking process with special formatting
- Reasoning Summaries - Brief reasoning recaps
- Tool Calls & Execution - ChatGPT tool usage tracking
- Tool Results - External tool response messages
- User Context - Profile and instruction context
- Code Blocks - Properly formatted code with syntax highlighting support
- YAML Frontmatter - Searchable metadata with title, creation date, update date, and tags
- Callout Syntax - Beautiful callouts for special content types:
> [!note]for internal reasoning> [!info]for reasoning summaries> [!abstract]for user context
- Embedded Images - Relative paths for portability
- Cross-Platform Compatibility - Works on Windows, macOS, and Linux
- Customize user and assistant display names
- Toggle Obsidian frontmatter and callouts
- Include or exclude conversation dates
- Custom date formatting
- Custom filename templates
- Configurable message separators
- Per-message timestamps with configurable format
- Skip empty messages option
- Optional
<details>formatting mode: markdown (default) or HTML-safe verbatim code block for maximum renderer compatibility
For users who prefer terminal/command-line tools:
- Python 3.7 or higher
- ChatGPT data export (see instructions below)
One-Command Install:
Windows:
curl -sL https://raw.githubusercontent.com/daugaard47/ChatGPT_Conversations_To_Markdown/main/install.bat -o install.bat && install.batMac/Linux:
curl -sL https://raw.githubusercontent.com/daugaard47/ChatGPT_Conversations_To_Markdown/main/install.sh | bashOR Manual Installation:
git clone https://github.com/daugaard47/ChatGPT_Conversations_To_Markdown.git
cd ChatGPT_Conversations_To_Markdown
pip install -r requirements.txt- Run setup wizard (first time only):
python setup.py- Convert conversations:
python chatgpt_json_to_markdown.py- Done! Open your
MarkdownFilesfolder
The file_name_format key in config.json controls how output filenames are constructed. The following tokens are supported:
| Token | Description | Example |
|---|---|---|
{title} |
Conversation title — alphanumeric only, spaces replaced with underscores | My_Conversation |
{display_title} |
Conversation title — alphanumeric only, spaces preserved | My Conversation |
{id} |
First 8 characters of the conversation ID (collision safety) | abc12345 |
{date} |
Conversation creation date, formatted with date_format |
2024-03-15 |
Default: {title}_{id} → My_Conversation_abc12345.md
Examples:
"file_name_format": "{title}_{id}"
"file_name_format": "{date}_{title}_{id}"
"file_name_format": "{date} - {display_title} ({id})"Note: Including
{id}in your format is recommended. It ensures conversations with identical titles never overwrite each other.
The line_endings key controls the line ending style written to .md files:
| Value | Line Ending | Use When |
|---|---|---|
native |
OS default (\r\n on Windows, \n on macOS/Linux) |
Default — matches your OS |
lf |
\n (Unix) |
Cross-platform vaults, Git repos, macOS/Linux Obsidian |
crlf |
\r\n (Windows) |
Windows-only vaults where tools expect CRLF |
"line_endings": "lf"Note:
lfis recommended for Obsidian vaults that are synced or version-controlled, as it avoids platform-specific diffs.
These options control how message headers, callouts, and timestamps are rendered. All defaults preserve the original output behavior — none of these need to be set unless you want to change something.
| Key | Default | Notes |
|---|---|---|
assistant_name |
"ChatGPT" |
Display name for assistant messages. Set to "" to suppress the assistant header entirely. |
user_name (set during setup) behaves the same way — set to "" to suppress the user header.
When use_obsidian_callouts is enabled, internal reasoning and reasoning summary blocks are rendered as Obsidian callouts. These options let you change the callout type or disable the callout entirely.
| Key | Default | Notes |
|---|---|---|
reasoning_callout_type |
"note" |
Callout type for internal reasoning blocks. "" = plain bold header instead. |
reasoning_callout_state |
"static" |
"collapsed" (closed by default) or "expanded" (open, collapsible) |
reasoning_summary_callout_type |
"info" |
Callout type for reasoning summary blocks. "" = plain bold header instead. |
reasoning_summary_callout_state |
"static" |
"collapsed" or "expanded" |
prompt_callout_type |
"" |
Wrap user prompts in a callout of this type. "" = plain bold header (default). |
response_callout_type |
"" |
Wrap assistant responses in a callout of this type. "" = plain bold header (default). |
tool_callout_type |
"" |
Wrap tool output in a callout of this type. "" = plain bold header (default). |
tool_callout_state |
"static" |
"collapsed" or "expanded" |
When a callout is used for a message, the callout title serves as the author header — no separate bold header is written.
Obsidian custom callout types allow you to style each block independently with a CSS snippet. For example, setting prompt_callout_type to "ai-prompt" and targeting [data-callout="ai-prompt"] in CSS lets you style user prompts to look like chat bubbles. Setting a callout type and using display: none in CSS is also a clean way to hide sections (such as tool output) without removing them from the file.
| Key | Default | Notes |
|---|---|---|
timestamp_tag |
"sub" |
HTML tag wrapping the timestamp. "time" for semantic markup, "" for no tag. |
timestamp_position |
"header" |
"header" places the timestamp on the line after the author header. "footer" places it after the message body. |
Timestamps stay attached to their message block regardless of position — including inside callout blocks when "footer" is used.
- Go to ChatGPT Settings → Data Controls
- Click "Export data"
- Wait for the email from OpenAI (usually arrives within a few hours)
- Download the ZIP file from the email
- Keep the ZIP file - no need to extract! The setup wizard will handle it.
Both methods will:
- ✅ Process all your conversations (could be 100s!)
- ✅ Organize by your chosen mode (flat/category/date/hybrid)
- ✅ Copy and organize all images →
Assets/Images/ - ✅ Copy and embed all audio →
Assets/Audio/ - ✅ Separate DALL-E images →
Assets/DALLE/ - ✅ Create markdown files with embedded media
- ✅ Generate Obsidian-compatible frontmatter
- ✅ Show progress during processing
The script supports 4 organization modes (choose during setup):
MarkdownFiles/
├── Assets/
│ ├── Images/ (all screenshots, uploaded images)
│ ├── Audio/ (all voice conversations)
│ └── DALLE/ (AI-generated images)
├── Starred/
│ └── 2025/
│ ├── 01-January/
│ └── 02-February/
├── Archived/
│ └── 2025/
│ └── 01-January/
└── Regular/
└── 2025/
├── 01-January/
├── 02-February/
└── 03-March/
Other modes: See the Organization Guide for Flat, By Category, and By Date modes.
Media Embedding:
- Images:
 - Audio:
<audio controls src="../../Assets/Audio/file_0000.wav"></audio> *Audio (11.3s)*
---
title: "My Conversation About React"
created: 2025-01-15 14:30:00
updated: 2025-01-15 16:45:00
tags:
- chatgpt
- conversation
---
# My Conversation About React
<sub>01-15-2025</sub>
---
**User**:
Can you help me understand React hooks?
**ChatGPT**:
Of course! React hooks are functions that let you use state...
**User**:

Can you explain this diagram?
**ChatGPT (thinking)**:
> [!note] Internal Reasoning
> **Analyzing diagram**: The user has shared a component lifecycle diagram...
**ChatGPT**:
This diagram shows the React component lifecycle...
**User**:
<audio controls src="Assets/file_000000002be0.wav"></audio> _Audio (5.2s)_
**ChatGPT**:
Sure! I can hear your question about useState...- Make sure you extracted ALL files from the ChatGPT export ZIP
- Verify that
file-*files and folders are in theJsonFilesdirectory - Check that paths in
config.jsonare absolute paths (e.g.,C:\Users\...on Windows)
Recent ChatGPT exports may split conversations into multiple files (for example: conversations-000.json, conversations-001.json, etc.).
Both the browser-based converter and the Python script support the legacy conversations.json format and the newer sharded format. The setup wizard and converter will detect whichever layout is present automatically.
- Use double backslashes in paths:
C:\\Users\\... - Or use forward slashes:
C:/Users/...
# Deactivate and recreate if needed
deactivate
rm -rf venv # or rmdir /s venv on Windows
python -m venv venv
venv\Scripts\activate # Windows
pip install tqdmPerfect integration with all features:
- Extract the converted ZIP file
- Open your Obsidian vault folder in File Explorer/Finder
- Drag the
MarkdownFilesfolder into your vault - Rename (optional): Change
MarkdownFilesto "ChatGPT Conversations" or anything you want - Refresh Obsidian: Press
Ctrl+R(orCmd+Ron Mac) - ✅ Done! Find conversations in the sidebar
What you get:
- ✅ Searchable YAML frontmatter (title, dates, tags)
- ✅ Collapsible thinking/reasoning sections
- ✅ Embedded images and audio that work offline
- ✅ All relative paths stay portable
Assets folder with your markdown files! Images/audio won't work if separated.
Good for sharing and collaboration:
- Extract the converted ZIP file
- Open Notion → Create a new page or database
- Import Click
⋯menu → Import → Markdown - Select all
.mdfiles fromMarkdownFiles - Upload the
Assetsfolder separately - ✅ Done! Conversations are now in Notion
Note: Some markdown formatting (like collapsible sections) may not transfer perfectly.
For graph-based organization:
- Extract the converted ZIP file
- Open your Logseq graph folder
- Copy contents of
MarkdownFilesinto yourpagesfolder - Move
Assetsfolder to your graph root - Re-index your graph
- ✅ Done! Conversations appear in your graph
Universal markdown compatibility:
- Extract the converted ZIP file
- Open
MarkdownFilesfolder in your editor - ✅ Done! Browse and edit your conversations
💡 Tip: The Assets folder contains all images and audio files
- ✅ Rename the
MarkdownFilesfolder to anything you want ⚠️ Never separate theAssetsfolder from your markdown files- 🔗 Portable paths: All image/audio links use relative paths
- 📊 Rich metadata: Each file has YAML frontmatter with title, dates, and tags
- 🗂️ Organization modes: Your chosen mode (Flat/Category/Date/Hybrid) determines the folder structure
✅ Included:
- All conversation text
- Images and screenshots you uploaded
- DALL-E generated images
- Audio messages - Embedded with playable HTML5 audio controls
- Tool calls and execution logs
- ChatGPT's internal reasoning (if available)
- User context and custom instructions
- Code blocks with formatting
- Video messages are shown as placeholders (audio track not currently extracted)
- ChatGPT's web browsing results show content but not raw data
This is a free and open-source project! Contributions are not just welcome - they're encouraged.
- 🐛 Report bugs - Open an issue if something doesn't work
- 💡 Suggest features - Tell us what would make this better
- 🔧 Submit pull requests - Fix bugs, add features, improve docs
- 📖 Improve documentation - Help others understand how to use this
- ⭐ Star the repo - Show your support!
- Test your changes - Make sure both HTML and Python versions work
- Update README - Document new features
- Keep it simple - This tool is meant to be easy to use
- Follow existing patterns - Match the current code style
Not sure where to start? Check the Issues page for ideas or open a new discussion!
This project is free and open source, available under the MIT License.
You can:
- ✅ Use it for personal projects
- ✅ Use it for commercial projects
- ✅ Modify and distribute it
- ✅ Build upon it
No attribution required (but appreciated!)
Need help?
- 📖 Check the Troubleshooting section above
- 🐛 Open an issue on GitHub
- 💬 Start a Discussion for questions
Found a bug or have an idea? We'd love to hear from you! This project gets better with community input.
- Created by daugaard47
- Built with help from the community
- Supports ChatGPT multimodal content and Obsidian optimization
If this tool helped you preserve your ChatGPT conversations:
- ⭐ Star this repo on GitHub
- 🐛 Report bugs to help improve it
- 💡 Share your ideas for new features
- 🔧 Contribute code if you're a developer
- 📢 Tell others who might find it useful
Enjoy your beautifully formatted ChatGPT conversations! 🎉
Free, open source, and built for the community.