AI & Agents

How to Extract Metadata from Email EML and MSG Files

Email metadata extraction reads header fields, routing information, timestamps, sender and recipient data, and attachment references from EML and MSG email file formats. This guide covers the structural differences between these two formats, walks through programmatic extraction with Python and Node.js, and explains how to manage extracted metadata for forensic, compliance, and archival workflows.

Fastio Editorial Team 10 min read
Structured audit log showing metadata fields and timestamps

What Email Metadata Contains

Every email carries two distinct layers of information: the visible message body and the hidden header block. The header block is the metadata layer, and it stores far more detail than most people realize. A typical email header includes over 20 distinct fields that document who sent the message, who received it, what route it took across mail servers, and when each handoff occurred.

Email metadata extraction reads these header fields, routing information, timestamps, sender and recipient data, and attachment references from saved email files. The two most common email file formats are EML (plain text, defined by RFC 5322) and MSG (Microsoft Outlook's proprietary binary format).

The core header fields defined by RFC 5322 include:

  • From: The sender's email address and display name
  • To, Cc, Bcc: All recipient addresses
  • Subject: The message subject line
  • Date: The timestamp when the message was composed
  • Message-ID: A globally unique identifier assigned by the originating mail server
  • In-Reply-To and References: Threading identifiers that link replies to their parent messages
  • Received: A chain of entries added by every mail server that handled the message, each recording its hostname, IP address, protocol, and timestamp

The Received header chain is particularly valuable. Each server that relays the message prepends its own Received entry, creating a reverse-chronological path from delivery back to origin. A message that passed through four mail servers will have four Received headers, and reading them bottom to top reconstructs the full delivery route.

Beyond RFC 5322 headers, modern email adds authentication results (SPF, DKIM, DMARC), content type declarations, MIME boundaries for attachments, and custom X-headers that mail services use for tracking and filtering. Legal and compliance teams handling email archives often centralize them in Fastio workspaces, where collaboration and AI-powered metadata extraction make message metadata searchable across the team.

How EML and MSG Formats Differ

The extraction approach depends entirely on which format you are working with, and the two formats differ at a fundamental level.

EML files are plain text. They follow the Internet Message Format defined in RFC 5322, storing headers as readable key-value pairs separated by colons, followed by the MIME-encoded message body. Any text editor can open an EML file and display its headers directly. This simplicity means that Python's built-in email module can parse EML files without any third-party dependencies.

MSG files use Microsoft's Compound Binary File Format (CFBF), also known as OLE2. This is the same container format used by older Office documents (.doc, .xls). MSG files store email data as a hierarchy of storage objects and streams within a binary container, with properties encoded using Microsoft's MAPI (Messaging Application Programming Interface) property schema. You cannot read an MSG file in a text editor, and parsing it requires a library that understands the OLE2 structure.

The practical differences for metadata extraction:

  • Parser requirements: EML files work with any RFC 5322 parser. MSG files need a CFBF/OLE2 parser with MAPI property mapping
  • Metadata scope: MSG files store Outlook-specific properties that EML files cannot represent, including categories, follow-up flags, importance levels, voting responses, and custom MAPI properties
  • Attachment handling: EML files encode attachments as MIME parts (typically base64). MSG files store attachments as binary streams within the OLE2 container
  • Character encoding: EML files declare encoding in Content-Type headers. MSG files use MAPI code page properties, which can cause issues on non-Windows systems
  • Conversion loss: Converting MSG to EML drops Outlook-specific metadata like categories, flags, and sensitivity labels because EML has no way to represent those fields

For archival and forensic work, preserving the original format matters. If you only need standard RFC 5322 headers, both formats provide them. If you need Outlook-specific properties, you must work with MSG files directly.

Structured data extraction interface showing parsed fields and values

Extracting Metadata from EML Files with Python

Python's standard library includes the email module, which can parse EML files without any additional packages. For header-only extraction, the HeaderParser class is faster because it skips body parsing entirely.

Here is a basic extraction script:

from email.parser import BytesParser
from email import policy
import json

def extract_eml_metadata(file_path):
    with open(file_path, "rb") as f:
        msg = BytesParser(policy=policy.default).parse(f)

metadata = {
        "from": msg["from"],
        "to": msg["to"],
        "cc": msg.get("cc"),
        "subject": msg["subject"],
        "date": msg["date"],
        "message_id": msg["message-id"],
        "in_reply_to": msg.get("in-reply-to"),
        "content_type": msg.get_content_type(),
    }

received_chain = msg.get_all("received", [])
    metadata["received_hops"] = len(received_chain)
    metadata["received_chain"] = received_chain

auth_results = msg.get("authentication-results")
    if auth_results:
        metadata["authentication_results"] = auth_results

attachments = []
    for part in msg.walk():
        if part.get_content_disposition() == "attachment":
            attachments.append({
                "filename": part.get_filename(),
                "content_type": part.get_content_type(),
                "size": len(part.get_payload(decode=True) or b""),
            })
    metadata["attachments"] = attachments

return metadata

This script extracts the standard header fields, counts the Received hops, captures authentication results, and catalogs every attachment with its filename, MIME type, and byte size.

For more structured output, the third-party eml_parser library normalizes parsed headers into a consistent dictionary format and extracts URLs, IP addresses, and domain names from the message body:

import eml_parser

with open("message.eml", "rb") as f:
    parsed = eml_parser.EmlParser().decode_email_bytes(f.read())

print(parsed["header"]["from"])
print(parsed["header"]["date"])
print(parsed["header"]["received_ip"])

The eml_parser library is useful when you need to extract network-level metadata like IP addresses from Received headers, which requires parsing the header values rather than just reading them.

Fastio features

Organize and Search Your Email Archives

Upload email files to Fastio and use Metadata Views to extract structured fields into a searchable, sortable database. No credit card required to get started.

Extracting Metadata from MSG Files with Python

MSG files require specialized parsing. The extract-msg library is the most widely used Python package for this purpose. It reads the OLE2 container, maps MAPI properties to Python objects, and exposes both standard email fields and Outlook-specific metadata.

Install it with pip:

pip install extract-msg

Basic metadata extraction:

import extract_msg

def extract_msg_metadata(file_path):
    msg = extract_msg.Message(file_path)

metadata = {
        "from": msg.sender,
        "to": msg.to,
        "cc": msg.cc,
        "subject": msg.subject,
        "date": str(msg.date),
        "message_id": msg.messageId,
        "importance": msg.importance,
        "sensitivity": msg.sensitivity,
    }

attachments = []
    for att in msg.attachments:
        attachments.append({
            "filename": att.longFilename or att.shortFilename,
            "size": len(att.data) if att.data else 0,
            "mime_type": att.mimetype,
        })
    metadata["attachments"] = attachments

msg.close()
    return metadata

Notice the fields that only MSG files provide: importance and sensitivity are MAPI properties with no EML equivalent. The library also exposes properties like body, htmlBody, and rtfBody for extracting the message content in different formats.

For batch processing, wrap the extraction in a directory scanner:

from pathlib import Path
import json

def batch_extract_msg(directory):
    results = []
    for msg_file in Path(directory).glob("*.msg"):
        try:
            meta = extract_msg_metadata(str(msg_file))
            meta["source_file"] = msg_file.name
            results.append(meta)
        except Exception as e:
            results.append({
                "source_file": msg_file.name,
                "error": str(e),
            })
    return results

On Linux and macOS, an alternative approach uses msgconvert from the libemail-outlook-message-perl package to convert MSG files to EML format first, then parse the EML output. This two-step process sacrifices Outlook-specific metadata but works well when you only need standard header fields and want to avoid the OLE2 parsing dependency.

Node.js and Command-Line Alternatives

Python is not the only option. Node.js and command-line tools each have strengths depending on your workflow.

Node.js with mailparser

The mailparser npm package handles EML files and returns structured objects with parsed headers, body text, and attachment metadata:

const { simpleParser } = require("mailparser");
const fs = require("fs");

async function extractEmlMetadata(filePath) {
  const source = fs.createReadStream(filePath);
  const parsed = await simpleParser(source);

return {
    from: parsed.from?.text,
    to: parsed.to?.text,
    subject: parsed.subject,
    date: parsed.date,
    messageId: parsed.messageId,
    attachments: parsed.attachments.map((a) => ({
      filename: a.filename,
      contentType: a.contentType,
      size: a.size,
    })),
  };
}

For MSG files in Node.js, the @nicbou/msg-reader and msg-reader packages parse the OLE2 container, though with fewer features than Python's extract-msg.

ExifTool

ExifTool reads metadata from EML files and returns structured output. It is not email-specific, but its broad format support makes it practical for mixed-format batch processing:

exiftool -json message.eml

This returns a JSON object with From, To, Subject, Date, and other header fields. ExifTool is a good choice when you are already using it for image or document metadata and want a single tool across all file types.

Forensic suites

For legal and incident response work, tools like Magnet AXIOM, X-Ways Forensics, and Autopsy include email parsers that extract metadata while maintaining chain of custody documentation. These tools handle both EML and MSG formats, plus PST and MBOX archives, and generate reports formatted for court submission.

Managing Extracted Email Metadata at Scale

Extracting metadata from a handful of email files is straightforward. The challenge grows when you need to process thousands of files across mixed formats, maintain an audit trail, and make the results searchable.

Structuring the output

Start by normalizing EML and MSG metadata into a common schema. Map Outlook-specific fields (importance, sensitivity, categories) into optional columns that stay empty for EML-sourced records. Store results in a structured format like JSON Lines, SQLite, or a spreadsheet where each row represents one email and each column represents one metadata field.

Common extraction pipeline

A practical pipeline for processing email archives follows this sequence:

  1. Scan the source directory and classify files by extension (.eml, .msg)
  2. Route each file to the appropriate parser
  3. Normalize the output into your common schema
  4. Validate required fields (every email should have From, Date, and Message-ID)
  5. Write results to your storage backend
  6. Flag files that failed parsing for manual review

Cloud-based metadata management

For teams that need to store, search, and share extracted metadata alongside the original email files, Fastio's Metadata Views can turn uploaded documents into a structured, queryable database. You describe the fields you want extracted in plain language, and the system builds a typed schema and populates a sortable spreadsheet across all files in a workspace. This works well for compliance teams that need to catalog email archives alongside contracts, invoices, and other documents in a single searchable interface.

For archival workflows where the original files also need to be preserved and shared, a workspace-based approach keeps the source emails, the extracted metadata, and the access permissions in one place. Intelligence Mode indexes the files for semantic search, so you can query across email content and metadata without building a separate search infrastructure.

Frequently Asked Questions

How do I extract metadata from an EML file?

Use Python's built-in email module. Open the EML file in binary mode, parse it with BytesParser, then access header fields like msg["from"], msg["subject"], and msg["date"]. For attachment metadata, iterate through msg.walk() and check for content disposition of "attachment". Third-party libraries like eml_parser provide additional features like IP address extraction from Received headers.

What metadata is stored in email headers?

Email headers contain over 20 fields defined by RFC 5322. The core fields are From, To, Cc, Subject, Date, and Message-ID. The Received header chain records every mail server that handled the message, including hostnames, IP addresses, and timestamps. Modern emails also include authentication headers (SPF, DKIM, DMARC results), Content-Type declarations, and MIME boundary markers for attachments.

How to read MSG file metadata without Outlook?

Use the extract-msg Python library. Install it with pip install extract-msg, then create a Message object from the file path. The library parses the OLE2 binary container and exposes fields like sender, to, subject, date, importance, and sensitivity as Python properties. On Linux, you can also convert MSG to EML using msgconvert from the libemail-outlook-message-perl package, then parse the resulting EML file with standard tools.

What tools extract metadata from email files for forensics?

For forensic work, ExifTool handles EML metadata extraction with JSON output. Dedicated forensic suites like Magnet AXIOM, X-Ways Forensics, and Autopsy parse EML, MSG, PST, and MBOX formats while maintaining chain of custody. For programmatic extraction, Python's email module and extract-msg library are the standard choices, often combined with hashing (SHA-256) to verify file integrity before and after extraction.

What is the difference between EML and MSG email files?

EML files are plain text files following the RFC 5322 standard. Any email client or text editor can read them. MSG files use Microsoft's proprietary CFBF binary format and store Outlook-specific metadata like categories, follow-up flags, and importance levels that EML cannot represent. Parsing EML requires only a standard RFC 5322 parser, while MSG requires an OLE2 library with MAPI property mapping.

Can I batch extract metadata from thousands of email files?

Yes. Write a Python script that scans a directory, classifies files by extension, routes each to the appropriate parser (email module for EML, extract-msg for MSG), normalizes the output into a common schema, and writes results to JSON Lines or a database. For large archives, process files in parallel using Python's concurrent.futures module and log any parsing failures for manual review.

Related Resources

Fastio features

Organize and Search Your Email Archives

Upload email files to Fastio and use Metadata Views to extract structured fields into a searchable, sortable database. No credit card required to get started.