How to export a ChatGPT conversation to Markdown
How to save a ChatGPT chat as Markdown: by hand or from the official export with a script — with code, tables and Obsidian front matter.
Markdown is the best format for AI chats: any editor reads it, code blocks keep their language, tables stay tables, and Obsidian, Notion and GitHub understand it without conversion. ChatGPT still has no "Save as Markdown" button. Here are two working ways to export a ChatGPT chat — by hand and with a script — plus the export that's coming soon to Chatsavo.
What ChatGPT does on its own
As of October 2026, ChatGPT has two built-in ways to get a conversation out:
| Method | What you get | Good for |
|---|---|---|
| "Copy" button under a reply | One reply's text as Markdown | One or two replies |
| Settings → Data controls → Export data | A ZIP with conversations.json and chat.html for your whole history |
Backups, bulk processing |
The official export arrives by email. It can take up to 7 days to prepare, and the link is valid for 24 hours (OpenAI Help). It's available on Free, Go, Plus and Pro, but not in Business or Enterprise workspaces. There are no Markdown files in the archive — just JSON and HTML (ChatGPT Exporter, as of October 2026).
Method 1. By hand, for one short chat
- Hover over a ChatGPT message and click Copy. The reply is copied with its Markdown: headings, lists and code blocks.
- Paste it into your editor (VS Code, Obsidian, Typora).
- Copy your own questions separately and mark them, say, as
### You.
This works for five or six messages. On a long chat you'll spend more time stitching than reading.
Method 2. From the official export with a script
To convert your entire history to Markdown, take conversations.json from the archive and run it through a script. The file's structure is internal and may change, but as of October 2026 each chat has a title, a create_time and a message tree called mapping.
A minimal Python script: one file per chat, roles labeled, branches flattened into a sequence.
import json, re, pathlib, datetime
data = json.load(open("conversations.json", encoding="utf-8"))
out = pathlib.Path("chatgpt-md"); out.mkdir(exist_ok=True)
for conv in data:
title = conv.get("title") or "Untitled"
nodes = conv.get("mapping", {}).values()
msgs = [n["message"] for n in nodes if n.get("message")]
msgs.sort(key=lambda m: m.get("create_time") or 0)
lines = [f"# {title}", ""]
for m in msgs:
role = m["author"]["role"]
parts = [p for p in m.get("content", {}).get("parts", []) if isinstance(p, str)]
text = "\n".join(parts).strip()
if role not in ("user", "assistant") or not text:
continue
lines += ["### You" if role == "user" else "### ChatGPT", "", text, ""]
date = datetime.date.fromtimestamp(conv.get("create_time") or 0)
name = re.sub(r'[\\/:*?"<>|]+', " ", title)[:80].strip()
(out / f"{date} {name}.md").write_text("\n".join(lines), encoding="utf-8")
Limits of this approach:
- it's a snapshot: new chats mean a new export;
- branches: if you edited questions,
mappingkeeps every version, and sorting by time will interleave them. To keep only the active branch, walk fromcurrent_nodeup to the root viaparent; - images and files come separately or as links, and you'll have to place them by hand.
For a one-off archive of your whole history, it's the best free route.
Coming soon: export in Chatsavo
Export to Markdown and other formats is coming soon to Chatsavo, in the next updates. It isn't available yet; here's how it will work.
Chatsavo will build Markdown from the same conversation you see on the page and handle the format's details:
- code blocks will keep their language: a Python block stays tagged
python; - tables will stay Markdown tables;
- formulas will be kept as LaTeX;
- links will survive;
- roles will be labeled "You" and "AI";
- the header will have the site, date and a link to the original chat.
Here's how we plan to format the "for Obsidian" export, with YAML front matter:
---
title: "Migrating to Postgres 17"
source: "ChatGPT"
url: "https://chatgpt.com/c/…"
created: 2026-09-14
updated: 2026-10-02
folder: "Work / Backend"
---
# Migrating to Postgres 17
### You
Which pg_upgrade changes should I check?
### AI
…
folder will come from your Chatsavo folder, so Obsidian search and Dataview will work on it right away.
Planned formats:
| Format | Use it for |
|---|---|
| Markdown | Any editor, GitHub, docs |
| Markdown for Obsidian | YAML front matter: site, dates, folder, link |
| Markdown for Notion | Markup that imports cleanly into Notion |
| HTML | Open in a browser or send to a colleague |
| TXT | Plain text, no markup |
What exists today: a full JSON download of all your Chatsavo data, free on every plan: Settings → Your data → "Download everything (JSON)". Files up to 25 MB are restorable in this version; larger downloads are export-only for now. When export ships, it'll be in the changelog.
Which method to pick
- One reply → ChatGPT's Copy button.
- Your whole history, once → official export plus a script.
- Regularly saving individual chats to Obsidian or Notion → the Copy button or a script for now; export in Chatsavo is coming soon.
Where to keep exported chats
A file on its own isn't worth much; what matters is finding it later. A few setups that work:
- Obsidian. A dedicated
AI/folder in your vault with topic subfolders. Front matter fields let Dataview build a table: every chat in "Work / Backend" for the month. - Notion. Markdown import creates a page per file. Handy for teams: a chat becomes a document people can comment on.
- Your project's Git repo. A
docs/decisions/folder with the conversations where architecture decisions were made. A year later, it beats anyone's memory.
Start file names with a date — 2026-10-02 Migrating to Postgres 17 (ChatGPT).md — so sorting by name matches chronology.
Common problems
Code blocks lost their language. That happens when you copy by selecting with the mouse: the browser copies rendered text, not the source markup. Use the Copy button under the message, or the official export.
A table turned into tab-separated text. Same cause. The Copy button keeps tables in Markdown syntax.
A long file name won't save. Chat titles can be long and contain / or :. The script above trims the name to 80 characters and replaces illegal characters; do the same in yours.
Markdown and Obsidian export is coming to Chatsavo in the next updates — follow the changelog. Folders and full-text search for ChatGPT and Claude work today: 7 days of Pro free, no card. How to install Chatsavo.
Find any old chat in a second
Chatsavo adds folders and full-text search to ChatGPT and Claude. Free, and chat text stays in your browser.