Home › Blog › Convert MBOX to CSV

How to Convert MBOX to CSV (Free: Python, Mac/Windows, or No MBOX at All)

Published September 28, 2026 · 12 min read
Export formats
Export formats
Gmail Exporter Guide
To convert MBOX to CSV for free, run a short Python script built on Python's standard mailbox and csv modules. It works on Mac, Windows and Linux, keeps your Gmail labels, and never uploads anything. Another option is to import the MBOX into Thunderbird and export the folder to CSV with the ImportExportTools NG add-on. If you haven't run Takeout yet, you can skip MBOX and export the emails you need from Gmail straight to CSV.

An MBOX from Google Takeout holds every message in full, but you can't sort or filter it like a spreadsheet. A CSV is the opposite: one row per email and one column per field, ready for Excel, Google Sheets or Numbers. This guide gives you a tested script that reads the Gmail labels most converters skip, a no-code route, and an honest comparison of each option.

What's inside an MBOX (and which fields make sense as CSV columns)

An MBOX file is one long text file with messages stored back to back. Each message starts with a line beginning From , followed by its headers and body. RFC 4155 describes this format. Attachments sit inside each message, encoded as text. That's why one file can grow to many gigabytes.

A CSV can't hold all of that neatly, so decide which fields you actually need. These map well to columns:

CSV columnWhere it comes fromNotes
dateDate headerConverted to ISO format (2026-03-03T10:15:00+01:00) so it sorts correctly
from_name, from_emailFrom headerSplit into two columns, which is easier for contact lists
to, ccTo, CcSeveral addresses in one cell
subjectSubjectDecoded from =?UTF-8?…?= encoding
gmail_labelsX-Gmail-LabelsGmail-specific; see below
attachment_count, attachment_namesMIME parts marked as attachmentsNames only; files aren't extracted
message_idMessage-IDUnique key, handy for removing duplicates
snippetFirst plain-text partTrimmed to 300 characters

About labels: Google's Takeout help says that each message's labels are kept in a special X-Gmail-Labels header, and that no mail client recognises it today. That's where your labels went when Thunderbird showed one flat folder. They're still in the file, and a script can read them.

Why not full bodies? One Excel cell holds at most 32,767 characters, and a sheet holds 1,048,576 rows. Long HTML emails break past the cell limit quickly. A snippet keeps the file light. If you need full text for analysis, JSON is a better container than CSV.

Method 1: Free Python script (standard library only)

This script uses only modules that ship with Python: mailbox to read the MBOX, csv to write the file, and email.header to decode accented subjects and names. You don't need to install anything, and your mail never leaves your computer.

Step by step

  1. Unzip your Takeout download (every part, if Google split it) and find the .mbox file for Mail. If you still need the file, see how Takeout creates the MBOX.
  2. Check Python 3. Open Terminal (Mac/Linux) and run python3 --version. On Windows, open Command Prompt and run py --version. If you get an error, install Python 3 from python.org.
  3. Save the script below as mbox_to_csv.py in the same folder as the MBOX.
  4. Run it: python3 mbox_to_csv.py "yourfile.mbox" emails.csv (on Windows: py mbox_to_csv.py "yourfile.mbox" emails.csv). Keep the quotes if the file name has spaces.
  5. Open emails.csv in Excel, Google Sheets or Numbers.

The script

#!/usr/bin/env python3
"""Convert a Gmail / Google Takeout MBOX file to a CSV you can open in Excel.

Usage:  python3 mbox_to_csv.py "All mail Including Spam and Trash.mbox" emails.csv
"""
import csv
import mailbox
import sys
from email.header import decode_header, make_header
from email.utils import getaddresses, parsedate_to_datetime

SNIPPET_CHARS = 300  # keep cells far below Excel's 32,767-character limit


def text(value):
    """Decode encoded headers such as =?UTF-8?B?...?= into readable text."""
    if value is None:
        return ""
    try:
        return str(make_header(decode_header(str(value))))
    except Exception:
        return str(value)


def iso_date(value):
    try:
        return parsedate_to_datetime(value).isoformat()
    except Exception:
        return text(value)  # keep the raw header if it can't be parsed


def body_snippet(msg):
    for part in msg.walk():
        if part.get_content_type() == "text/plain" and part.get_content_disposition() != "attachment":
            payload = part.get_payload(decode=True) or b""
            charset = part.get_content_charset() or "utf-8"
            try:
                body = payload.decode(charset, errors="replace")
            except LookupError:
                body = payload.decode("utf-8", errors="replace")
            return " ".join(body.split())[:SNIPPET_CHARS]
    return ""


def labels(msg):
    """Gmail labels from Takeout's X-Gmail-Labels header, e.g. 'Inbox,Important,Clients'."""
    return ",".join(l.strip() for l in text(msg["X-Gmail-Labels"]).split(",") if l.strip())


def attachments(msg):
    names = [text(p.get_filename()) or "(unnamed)" for p in msg.walk()
             if p.get_content_disposition() == "attachment"]
    return len(names), "; ".join(names)


def main(src, dst):
    columns = ["date", "from_name", "from_email", "to", "cc", "subject",
               "gmail_labels", "attachment_count", "attachment_names",
               "message_id", "snippet"]
    with open(dst, "w", newline="", encoding="utf-8-sig") as out:  # BOM = Excel reads UTF-8
        writer = csv.writer(out)
        writer.writerow(columns)
        count = 0
        for msg in mailbox.mbox(src, create=False):
            from_name, from_email = (getaddresses([text(msg["From"])]) or [("", "")])[0]
            n_att, att_names = attachments(msg)
            writer.writerow([
                iso_date(msg["Date"]), from_name, from_email,
                text(msg["To"]), text(msg["Cc"]), text(msg["Subject"]),
                labels(msg), n_att, att_names,
                text(msg["Message-ID"]), body_snippet(msg),
            ])
            count += 1
            if count % 5000 == 0:
                print(f"{count} messages...", file=sys.stderr)
    print(f"Done: {count} messages written to {dst}")


if __name__ == "__main__":
    if len(sys.argv) != 3:
        sys.exit('Usage: python3 mbox_to_csv.py input.mbox output.csv')
    main(sys.argv[1], sys.argv[2])

We tested this script with Python 3.11 on a sample MBOX that included an accented sender name, an encoded subject, multiple labels including a non-ASCII one, a PDF attachment, a Latin-1 body and a malformed date. Every row came out readable. If a date can't be parsed, the script keeps the raw text rather than dropping the message.

Handling encodings and UTF-8 for Excel

Email headers with accents or emoji are stored encoded, like =?UTF-8?Q?Factura_n=C2=BA_42?=. The script decodes them with decode_header, so you see "Factura nº 42". It writes the CSV as utf-8-sig, which is UTF-8 with a byte order mark. That mark is what lets Excel recognise the encoding when you double-click the file. Without it, Excel may show "José" instead of "José".

Commas and quotes inside fields, such as "Smith, Anna" <anna@…>, are escaped by Python's csv module according to the usual CSV conventions (RFC 4180), so columns don't shift. If your Excel uses semicolons as the list separator (common in European locales), import through Data › From Text/CSV and choose comma as the delimiter.

One MBOX quirk: body lines that begin with "From " are stored with a > in front, so the format can tell them apart from message boundaries. You may occasionally see ">From" in a snippet. That comes from the MBOX format, not from the script.

Large MBOX files (streaming)

The script reads one message at a time and writes its row straight away. It never loads the whole mailbox into memory. Python's mailbox.mbox first scans the file to find where each message starts, then reads messages one by one. In our test on a synthetic 274 MB MBOX with 30,000 messages (one in ten had a 50 KB attachment), the conversion took about 9 seconds with roughly 20 MB of peak memory. Real mailboxes and machines vary, so treat that as an order of magnitude, not a promise.

The practical ceiling is the spreadsheet, not Python. Past about a million messages you'll hit Excel's row limit, so split the output by year or by label, or load the CSV into a database. If Takeout split your mail into several MBOX files, run the script once per file and combine the CSVs.

No script, no MBOX

Export the emails you need straight to CSV from Gmail. It's free and runs in your browser.

Add to Chrome — It's Free

Method 2: Thunderbird + ImportExportTools NG (no code)

If you'd rather not touch a terminal, the free Thunderbird email client and its ImportExportTools NG add-on can do it. The add-on's documentation lists importing MBOX files and exporting all of a folder's messages as EML, HTML, PDF, CSV or plain text.

  1. Install Thunderbird. You can skip adding an email account and use Local Folders.
  2. Install ImportExportTools NG from Thunderbird's add-ons manager.
  3. Right-click Local Folders and use the add-on's menu to import your MBOX file. Our guide on how to open an MBOX file walks through this part.
  4. Right-click the imported folder, open the ImportExportTools NG menu, and choose the export option for the folder's messages in CSV format.
  5. Open the CSV in your spreadsheet. Test on a small folder first to see which columns your version writes.

The catch: Thunderbird imports a Takeout MBOX as one folder and ignores X-Gmail-Labels, as Google's note says of mail clients in general. You'll get senders, subjects and dates, but not your labels. For very large files the import can also be slow, since Thunderbird indexes every message.

Method 3: Online converters (read this first)

Search "mbox to csv converter" and you'll find web pages that ask you to upload the file. They're convenient, but think about what you're handing over: your entire mailbox, including bank notifications, password-reset links, contracts, medical or HR messages, and every attachment. You usually can't check how long the service keeps the file or who can access it.

If you do use one, use it only for mail you'd be comfortable publishing. Read the provider's privacy policy and retention terms. Never upload a work mailbox without your employer's approval. Under data-protection rules such as the GDPR, other people's emails in your inbox are personal data too. For almost everyone, Method 1 or 2 does the same job without the exposure.

Method 4: Skip MBOX — export from Gmail directly to CSV

If you haven't run Takeout yet, or it keeps failing (see Takeout not working?), you may not need an MBOX at all. The MBOX step exists because Takeout is built for backups, not spreadsheets. A browser extension can read the emails you're already viewing in Gmail, whether that's a label, a search or the inbox, and write a CSV on your device.

With Gmail Exporter, that means: run a Gmail search (for example from:billing@example.com after:2026/01/01), click Export, and a CSV downloads. See export Gmail to CSV directly for the steps, or get it into Excel as an .xlsx.

Here's what's free and what isn't. The free CSV has email, subject, body preview and sending service. Dates, sent/received direction, and names and phones from signatures come with the one-time Pro upgrade, which also adds Excel and JSON. So if you need dates and labels at no cost and already have the MBOX, the Python script is the better fit. If you want a quick contact list without Takeout, the extension is.

Comparison: which MBOX to CSV method should you use?

Python scriptThunderbird + add-onOnline converterDirect Gmail export
CostFreeFreeVariesFree core CSV; Pro one-time
PrivacyLocalLocalMailbox uploadedLocal, in browser
Needs Takeout MBOXYesYesYesNo
Gmail labelsYes (X-Gmail-Labels column)No (one folder)DependsExport per label or search
DatesYesYesDependsPro
Skill neededRun one commandInstall app + add-onNoneNone
Best forBig archives, custom columnsNon-coders who also want to read mailNon-sensitive test filesA subset of mail, no Takeout wait

After converting: quick checks

Frequently asked questions

Can Excel open an MBOX file?

Not in a useful way. MBOX is a mailbox format, not a table, so Excel would show raw message text rather than one row per email. Convert the MBOX to CSV first, then open the CSV in Excel.

How do I keep my Gmail labels in the CSV?

Google stores each message's labels in an X-Gmail-Labels header inside the Takeout MBOX. Read that header into its own column, as the script above does, then filter it in Excel with a "contains" text filter.

Does the Python method work on a Mac?

Yes. The script uses only Python's standard library, so it runs the same on macOS, Windows and Linux. On a Mac, run it from Terminal with python3.

How big an MBOX can Python handle?

The script processes one message at a time, so memory stays low. In our test, a 274 MB MBOX with 30,000 messages converted in about 9 seconds with roughly 20 MB of peak memory. The practical limit is Excel's 1,048,576 rows per sheet.

Are online MBOX to CSV converters safe?

They require uploading your whole mailbox, attachments included, to a third party's server. For personal or work mail, a local method such as the Python script or Thunderbird is the safer choice.