Your ChatGPT history. Clean Markdown. Zero memory overhead.
Install · Quick Start · Formats · Filtering · Config · Fields Reference
Takes your conversations.json from ChatGPT and turns it into clean, readable Markdown — or plain text, or structured JSON. Uses streaming parsing (ijson) so even 100MB+ exports never hit memory. Every aspect of filtering, formatting, and output is configurable through CLI flags or a single TOML file.
$ chatgpt-export export conversations.json --split subject --output-dir vault/
Exported 824 files to vault/
Tip
Export straight into your Obsidian vault for a fully searchable, linked archive of every conversation you've ever had with ChatGPT.
Requires Python
3.10+· managed with uv
git clone https://github.com/voidfreud/chatgpt-export-tool.git
cd chatgpt-export-tool
uv syncDev tooling (pytest, ruff, coverage)
uv sync --group devVerify:
uv run chatgpt-export --help# Analyze — stats without loading the whole file
uv run chatgpt-export analyze conversations.json
# Export to markdown on stdout
uv run chatgpt-export export conversations.json
# One markdown file per conversation
uv run chatgpt-export export conversations.json --split subject --output-dir exports/
# JSON dump
uv run chatgpt-export export conversations.json --format json --output dump.json| Format | Flag | Extension | |
|---|---|---|---|
| Markdown | --format md |
.md |
# headings, > blockquoted context, --- turn separators. Default. |
| Plain text | --format txt |
.txt |
Indented, plain labels — good for terminals and grep. |
| JSON | --format json |
.json |
Filtered conversation objects, valid JSON. |
Markdown and text exports follow the active conversation branch — you see the conversation as it played out, not the full tree with edits and branches.
Preview: Markdown output
# Opening bank account in Thailand
**ID:** 68ed8eba-1d00-832a-a2f1-4721496ed217
**Created:** 23:43 13-10-2025
**Turns:** 34
## Conversation Context
> User profile: The user provided the following information...
> User instructions: Speaking to Nova. Zero restraint...
## User [23:43 13-10-2025]
Can a foreigner open a Thailand bank account?
---
## Assistant [23:44 13-10-2025]
Yup — a foreigner *can* open a bank account in Thailand, but it's
*much more difficult* now than it used to be...Default transcript policy:
| Shown | Hidden | |
|---|---|---|
| User text, assistant text, thoughts, context (compact) | Tool plumbing, code execution, reasoning recap, blank nodes |
Fully configurable via [transcript] in the TOML config.
Structure and statistics — without writing output files.
uv run chatgpt-export analyze data.json
uv run chatgpt-export analyze data.json --fields # include field coverage
uv run chatgpt-export analyze data.json --verbose --output analysis.txt
uv run chatgpt-export analyze data.json --debugConvert conversations with full control over structure, metadata, and layout.
uv run chatgpt-export export data.json --fields "groups minimal"
uv run chatgpt-export export data.json --format json --split date --output-dir by-date/
uv run chatgpt-export export data.json --include "model*" --exclude plugin_ids
uv run chatgpt-export export data.json --config chatgpt_export.toml--fields all # everything (default)
--fields none # structure only
--fields "include title,create_time,mapping" # whitelist
--fields "exclude moderation_results" # blacklist
--fields "groups minimal" # named groupBuilt-in groups
| Group | Fields |
|---|---|
conversation |
_id conversation_id create_time update_time title type |
message |
author content status end_turn |
metadata |
model_slug message_type is_archived |
minimal |
title create_time message |
Full reference: Fields.md
Runs after structural filtering. Applies to keys inside message.metadata.
--include model_slug
--include "model*" --exclude plugin_ids # glob patterns| Mode | Flag | Output |
|---|---|---|
| single | --split single |
One stream or file (default) |
| subject | --split subject |
Title_ID.md per conversation |
| date | --split date |
Daily folders |
| id | --split id |
Named by conversation ID |
Note
--output is for single mode. Split modes use --output-dir.
Persist defaults in a single TOML file. The repo ships chatgpt_export.toml.example.
cp chatgpt_export.toml.example chatgpt_export.toml
uv run chatgpt-export export data.json --config chatgpt_export.tomlCLI flags always override TOML values.
TOML reference
[defaults] — format, split, fields, output directory, metadata
[transcript] — branch following and visibility
| Key | Default | |
|---|---|---|
show_system_messages |
false |
System prompts |
show_tool_messages |
false |
Tool/function calls |
show_assistant_code |
false |
Code execution |
show_reasoning_recap |
false |
Reasoning summaries |
user_editable_context_mode |
"compact" |
"compact" or "full" |
[text_output] — layout and formatting
| Key | Default | |
|---|---|---|
layout_mode |
"reading" |
"reading" or "compact" |
heading_style |
"markdown" |
"markdown" or "plain" |
turn_separator |
"---" |
Between turns |
strip_chatgpt_artifacts |
true |
Remove citation artifacts |
wrap_width |
88 |
0 to disable |
Presets
Reading-first (default)
[text_output]
layout_mode = "reading"
heading_style = "markdown"
strip_chatgpt_artifacts = true
wrap_width = 88Compact
[text_output]
layout_mode = "compact"
turn_separator = ""
wrap_width = 0Terminal
[defaults]
format = "txt"
[text_output]
heading_style = "plain"chatgpt_export_tool/
├── cli.py ← entry point
├── commands/ ← analyze, export
└── core/
├── parser.py ← streaming JSON (ijson)
├── filter_pipeline.py ← field + metadata filtering
├── export_service.py ← orchestration
├── config/ ← TOML loading & validation
├── transcript/ ← branch reconstruction
├── validation/ ← field & metadata checks
└── output/ ← formatters, writer, paths
Modular by design — filtering, formatting, splitting, and writing are separate concerns.
uv run pytest
uv run pytest --cov=chatgpt_export_tool --cov-report=term-missing
uv run ruff check chatgpt_export_tool tests
uv run ruff format --check chatgpt_export_tool testsMIT · built by @voidfreud