How to open ChatGPT conversations.json and convert it to Markdown (2026)
You requested your ChatGPT export, unzipped it, and found conversations.json: one giant file that is hard to read. Here is what is inside and how to turn it into normal notes.
1. Get the export
- In ChatGPT open your profile menu → Settings → Data controls.
- Under Export data click Export, then Confirm export.
- Open the email (or SMS) and click Download data export while signed in to the same account. The link expires 24 hours after it arrives, and exports can take up to 7 days.
2. What is in the ZIP
chat.html: every chat on one long page. Open it in a browser to read or search with Ctrl/Cmd+F. Large histories can make it slow.conversations.json: the structured data. Large exports may split it into numbered JSON files instead.
Why it looks odd: each chat stores messages as a tree (in a field called mapping), because edits and regenerations create branches. To get the thread you actually saw, you start at current_node and follow each message's parent back to the start.
3. Convert it to Markdown (free script)
Save this as convert.py in the same folder as conversations.json and run python3 convert.py. It writes one .md file per chat, with title and date frontmatter, into a chatgpt-markdown folder. It only reads files on your computer.
import json, re, pathlib, datetime
data = json.load(open("conversations.json", encoding="utf-8"))
out = pathlib.Path("chatgpt-markdown"); out.mkdir(exist_ok=True)
for c in data:
# Each chat is a tree of messages. Walk back from the last message
# you saw (current_node) to the root to get the visible thread.
node, msgs = c.get("current_node"), []
while node:
n = c["mapping"][node]
m = n.get("message") or {}
parts = (m.get("content") or {}).get("parts") or []
text = "\n".join(p for p in parts if isinstance(p, str)).strip()
role = (m.get("author") or {}).get("role")
if text and role in ("user", "assistant"):
msgs.append((role, text))
node = n.get("parent")
msgs.reverse()
title = c.get("title") or "Untitled"
date = datetime.datetime.fromtimestamp(c.get("create_time") or 0).strftime("%Y-%m-%d")
safe = re.sub(r"[^\w\- ]", "", title)[:60].strip() or "chat"
head = f'---\ntitle: "{title.replace(chr(34), chr(39))}"\ndate: {date}\nsource: chatgpt\n---\n\n'
body = "\n\n".join(f"**{'You' if r == 'user' else 'ChatGPT'}:**\n\n{t}" for r, t in msgs)
(out / f"{date} {safe} {str(c.get('id', ''))[:6]}.md").write_text(head + body, encoding="utf-8")
print("done:", len(data), "chats")Limits: it keeps text from you and ChatGPT and skips images, tool output and hidden system messages. OpenAI can change the format, so if a field is missing, open the JSON and adjust.
4. Put it in Obsidian
Drop the folder into your vault (for example AI/ChatGPT). The date and source frontmatter lets Dataview list chats by month. More tips in Export ChatGPT history to Markdown and Obsidian.
See ThreadVault or join the free waitlist
Related guides
- Back up all your AI chats (ChatGPT, Claude, Gemini, Perplexity)
- Save a ChatGPT chat as PDF
- Export Claude chats to Markdown and Obsidian
- Export ChatGPT history to Markdown / Obsidian
- Export Claude chats
- Export Gemini chats
- Export all your Perplexity threads
- ThreadVault vs AI Exporter
- ThreadVault vs AISaver
- ThreadVault vs ChatExports
Not affiliated with OpenAI. Export steps are from OpenAI's help center. Last checked October 27, 2026; features change, so verify in the app. Questions or corrections: hello@loomwise.app.