CSV is the universal solvent of business data. It opens in Excel, Sheets, Numbers, every BI tool, every CRM importer, every legacy system that's been running since 2009. Telegram, by contrast, exports HTML and JSON — useful for engineers, opaque for everyone else.
This guide covers three paths from Telegram to a clean CSV, the encoding gotchas that break exports for everyone who skips them, and a downloadable sample so you can test your pipeline before you point real data at it.
Why CSV at all
JSON is great for engineers. HTML is great for nobody — at least not as a data format. CSV is what your finance team can open, what your CRM imports without a custom mapper, what your data-analyst intern can pivot in Sheets.
If your downstream is a Postgres ingest, sure, use JSON. If your downstream is a human, use CSV. Most exports are the second case.
What Telegram's native export gives you
Telegram Desktop has a built-in export buried under Settings → Advanced → Export Telegram Data. You can choose HTML or JSON. Both are everything-or-nothing: all your chats, all your media, no per-group control, no date range.
The HTML output is human-readable — open messages.html in a browser and scroll. It's a backup. It's not analyzable.
The JSON output is parseable but nested in a shape that needs flattening before it touches a spreadsheet. A typical message looks like:
{
"id": 12345,
"type": "message",
"date": "2026-05-08T14:22:11",
"from": "Jane Doe",
"from_id": "user12345",
"text": "Welcome to the group!"
}
Multiply by 200,000 messages and you've got 80MB of JSON to flatten. Doable, not delightful.
Neither output is CSV-ready. That's the gap this guide fills.

Three paths to a clean CSV
| Path | Best for | Time investment |
|---|---|---|
| Convert native JSON | One-time pulls, technical user | 20–60 min |
| Custom Python script | Recurring exports, complex shaping | 1–2 hours setup |
| Browser extension (Mastros) | Recurring or ad-hoc, no setup | 60 seconds |
Path A — Convert the native JSON
If you already have the JSON dump and just need a one-off CSV, a 30-line script will get you there:
import json, csv
with open('result.json') as f:
data = json.load(f)
with open('messages.csv', 'w', newline='', encoding='utf-8-sig') as f:
w = csv.writer(f)
w.writerow(['id','date','from','from_id','text'])
for chat in data['chats']['list']:
for m in chat.get('messages', []):
if m.get('type') != 'message':
continue
text = m.get('text', '')
if isinstance(text, list): # text can be a list of styled fragments
text = ''.join(t if isinstance(t, str) else t.get('text','') for t in text)
w.writerow([m['id'], m['date'], m.get('from',''), m.get('from_id',''),
text.replace('\n', ' ')])
Watch out for the text field — Telegram returns a list of styled fragments when there's bold, italic, links, or mentions in the message, and a plain string otherwise. Flattening both shapes is the only fiddly part.
Path B — Write a fresh script with Telethon
If you don't have a JSON dump and you want repeatability, skip the conversion and pull straight to CSV with Telethon. This is covered in our member-export guide — the same client, different API call (iter_messages instead of GetParticipantsRequest).
Path C — Mastros → spreadsheet in 60 seconds
The Telegram CSV export is the whole of this path: install, open the chat on Telegram Web, pick what to save, download the file.
Open Telegram Web, click the Mastros toolbar icon, choose what to export (members, messages, media metadata), pick CSV, hit Go. The file lands in your Downloads folder. Drag it into Sheets. Done.
Behind the scenes: Mastros runs the same MTProto API calls Telethon would, but inside your browser tab — no credentials handshake, no Python, no server. Practically every benchmark we've run has Mastros faster than the equivalent Python script for the simple reason that it doesn't need to spin up a process or re-authenticate.
Encoding gotchas — the unsexy stuff that breaks exports
This is where most homegrown scripts fall over. Three rules:
1. UTF-8 with BOM, always
Without the BOM, Excel renders 中文, русский, 우리, العربية, and emoji as garbled boxes. Sheets is more forgiving but still mangles a few edge cases. The fix is one character — the byte-order mark at the start of the file:
open('out.csv', 'w', encoding='utf-8-sig') # the -sig variant adds the BOM
Mastros writes with BOM by default. Your homegrown script probably doesn't. If your CSV looks fine in Notepad and broken in Excel, this is why.
2. Phone numbers as text, not numbers
+44 7700 900123 becomes 4.47e10 in Excel if you let the spreadsheet infer types. Solutions, in order of preference:
- Prefix with an apostrophe in the cell (
'+44 7700 900123) — Excel reads as text. - Wrap in
="…"syntax (="+447700900123") — also forces text. - Train your downstream to use Sheets, which handles
+-prefixed strings correctly.
Mastros uses the apostrophe-prefix approach because it survives a round-trip through Excel.
3. Line breaks inside bios and messages
Replace \n with ¶ or just a space. Do not let raw newlines into a CSV cell unless you enjoy debugging at 11pm. Strict CSV allows quoted newlines, but half the world's importers don't.
text.replace('\r', ' ').replace('\n', ' ') # belt and suspenders
The CSV pitfalls every script hits
Beyond the big three, a handful of smaller landmines:
Commas in bios ("Founder, builder, dad") — quote the field. Python's csv module does this automatically; if you're hand-rolling, use csv.writer instead of f.write.
Quotes inside fields — double them. She said "hi" becomes "She said ""hi""". Again, csv.writer handles it.
Trailing whitespace from copy-paste — strip on read. df['username'].str.strip() in pandas, or s.strip() in raw Python.
Inconsistent column counts — your script crashed mid-export and resumed without truncating; rerun, don't patch. A CSV with 9 columns on row 4,801 and 10 columns elsewhere will silently corrupt every downstream join.
Trailing commas vs. trailing empties — a,b, and a,b,"" look the same in Excel but parse differently in pandas. Pick one; standardize.
Sample dataset
Grab a 200-row sample CSV (members of a fictional crypto community) below. Use it to test your import pipeline before you point real data at it.
The sample includes:
- All field types (numeric IDs, Unicode names, emoji-laden bios, blank phones)
- Edge cases (commas in bios, quotes in messages, RTL languages, mixed-script display names)
- A few rows of bots — useful for testing filter logic
- 200 rows total — enough to feel real, small enough to inspect manually
A 60-second test of your pipeline
Once you have the sample:
- Drop into Sheets — does it open with columns aligned and emoji intact?
- Pivot by
role— do you getmember,admin,creatorand notmember,admin? - Filter
is_bot = TRUE— do you get the four bots and not 0 or 12? - Sort by
join_datedesc — are the recent rows at the top, dates not jumbled by string-sort?
If any of those fail, you've got an encoding or quoting problem to fix before you process real data.
When CSV stops being the right format
CSVs scale fine to a few hundred thousand rows. Past that, columnar formats (Parquet, Arrow) load faster and compress better. If you're piping daily Telegram exports into a warehouse, switch to Parquet at the ingestion step — Mastros has a Parquet output for paid tiers, or convert post-hoc with pandas.read_csv(...).to_parquet(...).
For pure analyst use cases under a million rows? Stay on CSV. The portability beats the performance gain.
Install the Telegram Scraper → mastros.online/telegram-scraper — get your first clean CSV out of Telegram in 60 seconds, encoding handled.
FAQ
Why does my Telegram CSV show garbled characters in Excel?
The file is missing its byte-order mark. Without a BOM, Excel renders Chinese, Russian, Korean, Arabic, and emoji as boxes — Sheets is more forgiving but still mangles edge cases. In Python the fix is one flag: open the file with encoding='utf-8-sig' instead of utf-8. If the CSV looks fine in Notepad but broken in Excel, this is always the reason.
Why did Excel turn my phone numbers into scientific notation?
Excel infers types on open, so +44 7700 900123 becomes 4.47e10. The most reliable fix is to prefix the value with an apostrophe in the cell, which forces Excel to treat it as text and survives a round-trip through the spreadsheet.
How should line breaks inside bios and messages be handled?
Replace them before writing, with a space or a pilcrow. Strict CSV does permit quoted newlines, but a large share of real-world importers mishandle them, so raw newlines in cells are a reliable source of late-night debugging.
Does Telegram export CSV directly?
No. The native export produces HTML or JSON — useful to engineers, opaque to everyone else. Getting to CSV means converting the JSON, writing a script against the API, or using a tool that outputs CSV directly.
What breaks CSV exports most often?
Commas and quotes inside bios (use a real CSV writer rather than string concatenation — it quotes and escapes for you), trailing whitespace from copy-paste, and inconsistent column counts after a script crashed and resumed without truncating. That last one is the dangerous case: a row with nine columns among tens, and every downstream join silently corrupts.
At what point should I stop using CSV?
CSV is fine to a few hundred thousand rows. Past that, columnar formats like Parquet or Arrow load faster and compress far better, so switch at the ingestion step if you are piping exports into a warehouse daily. For analyst work under a million rows, portability still beats the performance gain.
Related reading
- How to Export Telegram Group Members to CSV — the member-export companion to this guide.
- How to Export Telegram Messages: 5 Methods Compared — when the data you're after is messages, not members.
- Telegram API vs Browser Extension — the build-vs-buy framing for technical evaluators.