The most reliable scripted approach on a Windows machine is to use Python to read the MBOX file and use the Outlook COM interface (pywin32) to inject the emails directly into a new PST archive.
- You must run this on Windows with Microsoft Outlook installed.
- Install the required Python library:
pip install pywin32
The Python Script
Save the code below as mbox_to_pst.py. It reads your MBOX file message by message and adds them to a newly created PST file via Outlook.
import os
import mailbox
import win32com.client
from email import policy
from email.parser import BytesParser
# --- CONFIGURATION ---
SOURCE_MBOX_DIR = r"C:\path\to\your\MBOX_Folder" # Directory containing .mbox files/folders
OUTPUT_PST_PATH = r"C:\path\to\your\output.pst"
# ---------------------
def get_or_create_folder(parent_folder, folder_name):
"""Safely finds or creates a subfolder in Outlook."""
try:
return parent_folder.Folders.Item(folder_name)
except Exception:
return parent_folder.Folders.Add(folder_name)
def process_mbox_file(mbox_path, target_outlook_folder):
"""Parses an MBOX file and maps formatting/attachments to Outlook."""
print(f"Processing: {os.path.basename(mbox_path)}")
# Open file in binary mode to accurately extract attachments and formatting
with open(mbox_path, 'rb') as f:
mbox = mailbox.mbox(f)
for index, msg_bytes in enumerate(mbox.itervalues(), 1):
try:
# Parse full message with formatting and layers intact
msg = BytesParser(policy=policy.default).parsebytes(msg_bytes.as_bytes())
# Create a blank mail item in the specific PST subfolder
mail_item = target_outlook_folder.Items.Add(0) # 0 = olMailItem
# Metadata
mail_item.Subject = msg['subject'] or "(No Subject)"
mail_item.To = msg['to'] or ""
mail_item.CC = msg['cc'] or ""
mail_item.BCC = msg['bcc'] or ""
# Handle Formatting (HTML vs Plain Text)
html_body = msg.get_body(preferencelist=('html'))
text_body = msg.get_body(preferencelist=('plain'))
if html_body:
mail_item.HTMLBody = html_body.get_content()
elif text_body:
mail_item.Body = text_body.get_content()
# Handle Attachments
for part in msg.iter_attachments():
filename = part.get_filename()
if filename:
# Temporary save attachment to disk to load into Outlook
temp_path = os.path.join(os.environ['TEMP'], filename)
with open(temp_path, 'wb') as at_file:
at_file.write(part.get_content())
mail_item.Attachments.Add(temp_path)
os.remove(temp_path) # Clean up
# Save into the PST
mail_item.Save()
except Exception as e:
print(f" Error processing email #{index} in {os.path.basename(mbox_path)}: {e}")
def convert_all_mbox_to_pst():
if not os.path.isdir(SOURCE_MBOX_DIR):
print(f"Error: Source directory not found at {SOURCE_MBOX_DIR}")
return
print("Opening Outlook...")
outlook = win32com.client.Dispatch("Outlook.Application")
namespace = outlook.GetNamespace("MAPI")
print(f"Mounting PST at: {OUTPUT_PST_PATH}")
namespace.AddStore(OUTPUT_PST_PATH)
pst_store = namespace.Stores.Item(namespace.Stores.Count)
root_folder = pst_store.GetRootFolder()
# Traverse directory to reconstruct folder structures
for root, dirs, files in os.walk(SOURCE_MBOX_DIR):
for file in files:
if file.endswith('.mbox'):
full_mbox_path = os.path.join(root, file)
# Determine relative path structure
rel_path = os.path.relpath(root, SOURCE_MBOX_DIR)
current_target_folder = root_folder
if rel_path != ".":
# Replicate nested folder path in Outlook
for folder_part in rel_path.split(os.sep):
current_target_folder = get_or_create_folder(current_target_folder, folder_part)
# Create a specific folder for the file itself (minus .mbox extension)
mbox_folder_name = os.path.splitext(file)[0]
final_email_folder = get_or_create_folder(current_target_folder, mbox_folder_name)
# Import
process_mbox_file(full_mbox_path, final_email_folder)
print("Detaching PST from Outlook...")
namespace.RemoveStore(root_folder)
print("Migration complete!")
if __name__ == "__main__":
convert_all_mbox_to_pst()
Close Outlook before running the script so it can control the background automation smoothly. Because this interacts live with Outlook's engine, it can take some time if you have thousands of emails. For enormous archives (tens of gigabytes), you may want to look into modular toolkits like the mbox-to-pst-toolkit on GitHub which exports to raw .eml intermediate files first to prevent memory bottlenecks.
Note: PST is a proprietary Microsoft format, meaning Python cannot generate a true .pst file purely on its own without interacting with external libraries. That is why the above approach is actually using Outlook itself as part of the process. The script merely automates it all.
Easier Alternative
If the Python script fails or messes up email timestamps, the most accurate native approach is to install Mozilla Thunderbird (free), import your MBOX files using the ImportExportTools NG extension, configure your email account via IMAP, and let them sync naturally to the server so Outlook can download them flawlessly.