A Python library to interact with local Mozilla Thunderbird profiles and mail client data on Linux.
Project description
pythonbird
A lightweight, zero-dependency Python library for reading local Mozilla Thunderbird profiles, mailboxes, address books, and opening native compose windows on Linux.
Current version: 0.3.0
Features
- Detects standard, Snap, and Flatpak Thunderbird profiles.
- Supports explicitly selected or backed-up profile directories.
- Reads configured accounts from
prefs.js. - Discovers local and IMAP Mbox folders, including nested
.sbdfolders. - Returns typed
Message,Attachment, andContactobjects. - Searches by addresses, subject, content, dates, attachments, flags, and tags.
- Reads Thunderbird read, starred, replied, and forwarded status metadata.
- Reads Thunderbird tags from
X-Mozilla-Keys. - Saves attachments, EML files, decoded text bodies, and HTML bodies.
- Exports folders to JSON.
- Reads SQLite address books in read-only mode.
- Opens Thunderbird compose windows without using a system shell.
- Preserves the dictionary-based API from pythonbird 0.1.x.
Requirements
- Linux
- Python 3.9 or newer
- Mozilla Thunderbird only when opening compose windows
Reading an explicitly supplied profile or backup does not require Thunderbird to be installed.
Installation
pip install pythonbird
or:
poetry add pythonbird
Quick start
from pythonbird import Thunderbird, __version__
print(__version__)
tb = Thunderbird()
print(tb.profile_dir)
print(tb.accounts())
print(tb.folders())
for message in tb.messages("Inbox", limit=20):
print(message.subject, message.sender, message.read)
Use an explicit profile when automatic detection is not appropriate:
tb = Thunderbird(
profile_dir="/home/user/.thunderbird/example.default-release",
command=["thunderbird"],
)
Folders
Canonical folder names use / for nesting:
for folder in tb.folders():
print(folder)
messages = tb.messages("Archive/2026")
A unique short folder name is accepted. When several folders have the same short name, use the full name returned by folders().
Searching messages
from datetime import date
results = tb.search(
"Inbox",
sender="github.com",
recipient="example.com",
subject="report",
contains="release",
after=date(2026, 1, 1),
before=date(2026, 12, 31),
has_attachments=True,
unread=True,
starred=True,
replied=False,
forwarded=False,
tags=["work", "important"],
limit=50,
)
For large mailboxes, use the iterator:
for message in tb.iter_search("Inbox", unread=True):
print(message.subject)
Tag matching is case-insensitive and all requested tags must be present. Messages without a valid Thunderbird status do not match unread=True or unread=False.
Message metadata
message = tb.messages("Inbox", limit=1)[0]
print(message.sender)
print(message.recipients)
print(message.subject)
print(message.date)
print(message.text_body)
print(message.html_body)
print(message.read) # True, False, or None when status is unavailable
print(message.starred)
print(message.replied)
print(message.forwarded)
print(message.tags)
Status and tag support is read-only. Version 0.3.0 does not modify Mbox files or Thunderbird indexes.
Attachments and export
message.save_attachments("downloads")
message.save_eml("exports/message.eml")
message.save_text("exports/message.txt")
message.save_html("exports/message.html")
tb.export_json("exports/inbox.json", folder="Inbox", limit=100)
Existing files are not overwritten unless overwrite=True is explicitly passed to a model save method.
Contacts
for contact in tb.contacts():
print(contact.name, contact.email)
Read a different Thunderbird address book:
contacts = tb.contacts(
database_path="/path/to/address-book.sqlite",
strict=True,
)
With strict=False, SQLite and schema errors produce an empty list instead of raising ThunderbirdContactsError.
Compose window
tb.compose(
to="developer@example.com",
subject="Created with pythonbird",
body="Hello from pythonbird!",
attachment_path="/path/to/report.pdf",
)
The compose window is opened through an argument list without shell=True. The attachment must exist.
Low-level and legacy APIs
The public low-level classes remain available:
from pythonbird import ThunderbirdContacts, ThunderbirdLinux, ThunderbirdMail
client = ThunderbirdLinux()
mail = ThunderbirdMail(client)
contacts = ThunderbirdContacts(client)
The 0.1.x dictionary API is preserved:
messages = mail.get_local_inbox_messages(limit=20)
for message in mail.iter_mbox_messages("/path/to/mbox"):
print(message["subject"])
Safety and limitations
- pythonbird 0.3.0 reads mail and address-book data but does not change Mbox files.
- Thunderbird metadata headers may be absent or stale;
readis therefore optional. - IMAP folders must be present in the local Thunderbird profile to be readable.
- Calendar, SMTP sending, watchers, and Windows/macOS profile support are not included.
- Work with a backup when inspecting important profiles from custom scripts.
See GUIDE.md for the complete API reference and CHANGELOG.md for release history.
Development
poetry install
poetry run pytest
poetry run black --check .
poetry run flake8 pythonbird tests
poetry build
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pythonbird-0.3.0.tar.gz.
File metadata
- Download URL: pythonbird-0.3.0.tar.gz
- Upload date:
- Size: 13.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d580046a5a9694335e2f19f065ee3afc7c6c28960e7937821d2eedd6647813f2
|
|
| MD5 |
d1f33a83618c2ba45aa0c2450bedb8af
|
|
| BLAKE2b-256 |
42fabde922a510709fc2c7c8ee8ebf5705f55b46d6e3cd038c3dd72ea2cb8fbd
|
Provenance
The following attestation bundles were made for pythonbird-0.3.0.tar.gz:
Publisher:
publish.yml on rchbld/pythonbird
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pythonbird-0.3.0.tar.gz -
Subject digest:
d580046a5a9694335e2f19f065ee3afc7c6c28960e7937821d2eedd6647813f2 - Sigstore transparency entry: 2211556544
- Sigstore integration time:
-
Permalink:
rchbld/pythonbird@4e41757e188f0141b4d0c718758d541d1b2e170b -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/rchbld
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4e41757e188f0141b4d0c718758d541d1b2e170b -
Trigger Event:
release
-
Statement type:
File details
Details for the file pythonbird-0.3.0-py3-none-any.whl.
File metadata
- Download URL: pythonbird-0.3.0-py3-none-any.whl
- Upload date:
- Size: 14.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
681b3613d4708c3e3b5df2b735431c3052a36d9a47ada01eb581c8ea7d35dc5d
|
|
| MD5 |
05e9ea319b5bce9efd3b0cbd8fdda516
|
|
| BLAKE2b-256 |
2b0ab23de7ce16bda83e7ec39b961cfa32cb4636864e07d1fdb91282a17dc5b6
|
Provenance
The following attestation bundles were made for pythonbird-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on rchbld/pythonbird
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pythonbird-0.3.0-py3-none-any.whl -
Subject digest:
681b3613d4708c3e3b5df2b735431c3052a36d9a47ada01eb581c8ea7d35dc5d - Sigstore transparency entry: 2211556578
- Sigstore integration time:
-
Permalink:
rchbld/pythonbird@4e41757e188f0141b4d0c718758d541d1b2e170b -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/rchbld
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4e41757e188f0141b4d0c718758d541d1b2e170b -
Trigger Event:
release
-
Statement type: