Skip to main content

gmail-cleanup

tests release pypi python license

Reclaim your Gmail inbox — safely. One-click unsubscribe (RFC 8058), declarative filter management, scheduled autopilot, and a hard-coded KEEP list that physically cannot unsubscribe you from your bank.

$ gmail-cleanup autopilot

🤖 gmail-cleanup autopilot — full inbox cleanup
━━━ Phase 1/4: Apply Gmail filters ━━━
   ✅ 8 upgraded, 4 created (all archive + mark read)
━━━ Phase 2/4: Unsubscribe noise senders (last 30d, min-count 2) ━━━
   ✅ 25 unsubscribed via RFC 8058 one-click POST, 61 inbox messages archived
━━━ Phase 3/4: Mark archived-unread as read ━━━
   ✅ 4,710 messages marked read
━━━ Phase 4/4: Verify previously-unsubscribed senders ━━━
   ⚠️  2 stuck (re-run with --escalate to auto-block)
   ✅ 18 silent (unsubs stuck)

🎉 Autopilot complete. Inbox: 144 (was 7,283) · Unread: 150 (was 4,858)

✨ Why it's different

Other tools gmail-cleanup
Safety Best-effort KEEP list refuses to unsub from banks, .gov, healthcare
Unsubscribe Click the link in the email RFC 8058 one-click POST — the modern standard
Real humans At risk of getting archived Whitelist-protected — starred, important, spam-shielded
Reversibility Often destructive Archive over delete by default — recoverable from All Mail
Auditability Black box 84-test safety net + structured logs + state file
Automation Manual, daily One-command autopilot, optional daily scheduler (macOS)

📦 Installation

End users — isolated install via pipx (recommended):

pipx install gmail-inbox-cleanup

The PyPI package is gmail-inbox-cleanup (the gmail-cleanup name on PyPI belongs to an unrelated project); the command it installs is gmail-cleanup.

Developers — clone and edit:

git clone https://github.com/bgorzelic/gmail-cleanup.git
cd gmail-cleanup
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

Requirements: Python 3.11+ and a Google Cloud project with the Gmail API enabled (the setup wizard walks you through both).

💡 Hitting OAuth errors? See TROUBLESHOOTING.md. The most common gotcha (consent screen / test users for unverified apps) is handled by the setup wizard in v0.5.1+.


🚀 Quick start

# First time — interactive wizard handles GCP, OAuth, config in 7 steps:
gmail-cleanup setup

# Daily use — one command runs the full cleanup pipeline:
gmail-cleanup autopilot

# Glance at inbox health:
gmail-cleanup status

# Two weeks later — escalate any unsubs that didn't stick:
gmail-cleanup autopilot --escalate

--dry-run previews any destructive command. --quiet for cron-friendly silent runs. --verbose for debug.


🧠 How it works

Every sender falls into one of three buckets:

                ┌──────────────────────────────────┐
  inbox mail ──▶│ Real human (humans.yaml)         │──▶  ⭐ star + important + spam-shield
                ├──────────────────────────────────┤
                │ Must-keep automation (keep.yaml) │──▶  🔒 NEVER unsubscribe (safety rail)
                │  banks, .gov, health, brokerage  │
                ├──────────────────────────────────┤
                │ Noise (kill.yaml + auto-discover)│──▶  🗑  one-click unsub + archive
                └──────────────────────────────────┘

Tune the buckets by editing the YAML files in ~/.gmail_cli/lists/ — no Python required.

How a sender gets picked

No AI, no guessing — deterministic rules over email headers, so every decision is explainable:

  1. Group inbox mail by sender (From header).
  2. Target a sender if it sent at least --min-count messages in the window, or matches kill.yaml.
  3. Protect: drop anyone in humans.yaml (exact match — always wins), then anyone matching keep.yaml (substring).
  4. Require a List-Unsubscribe header. No header means a person or a transactional sender — skipped, not archived.
  5. Unsubscribe by the best method offered: RFC 8058 one-click POST → HTTPS GET → mailto:.
  6. Archive that sender's inbox mail (never delete).
  7. Verify later: mail still arriving after the unsubscribe plus a 2-day grace period marks the sender stuck; --escalate then blocks it.

Gentle on the Gmail API

Scans are batched and paced to Google's documented per-user quota (100 units/s), back off on rate limits (honoring Retry-After), and remember a throttle so a scheduled run never piles on. A 3,000-message inbox scans in about 10 minutes.


🛠 The autopilot pipeline

gmail-cleanup autopilot
   │
   ├── Phase 1  filters apply        Declare categorization rules to Gmail (idempotent)
   ├── Phase 2  unsubscribe          Find recent noise, hit RFC 8058 one-click, archive
   ├── Phase 3  mark-read            Clear the archived-but-unread backlog
   └── Phase 4  verify               Audit previous unsubs; --escalate to block stuck ones

Each phase also works standalone. Autopilot is the convenience composition.


📖 Commands

Command What it does
setup Interactive 7-step wizard: GCP credentials → OAuth → account registration. Start here.
autopilot Full pipeline. The daily driver. --days N / --min-count K tune the unsubscribe phase; --email-summary emails the report to you (try it with --dry-run); --all-accounts runs across every configured Gmail.
status Dashboard: live counts, filter inventory, list sizes, 7-day history
stats Inbox count, unread, storage, oldest email
top-senders --days N Rank senders by volume
subscriptions Find senders with List-Unsubscribe headers
unsubscribe --days N --min-count K One-click unsubscribe noise senders + archive their mail
mark-read --query Q Bulk-mark messages as read (default: archived-but-unread backlog)
verify [--grace-days N] [--escalate] Check whether senders kept mailing after you unsubscribed (plus a 2-day grace period); auto-block stuck senders
attachments [--archive|--delete] Find oversized old emails, rank by bytes
filters apply / list Create/upgrade/list Gmail filters
config show / init Manage ~/.gmail_cli/config.yaml
accounts list / add / remove Manage multi-account roster
schedule install / uninstall / status Daily launchd-scheduled autopilot (macOS)
archive / delete / label Bulk operations by sender / category / label / query / age

All destructive commands prompt for confirmation unless you pass --yes. Global flags: --quiet, --verbose, --all-accounts.


🔐 Safety model

Four YAML lists govern behavior. Each one is the packaged seed (shipped inside the package) merged with your own copy in ~/.gmail_cli/lists/<name>.yaml. Edit your copies to tune the tool — no Python required. Everything the tool writes goes to ~/.gmail_cli/lists/, never into the installed package.

File Used by Match Seed ships Behavior
keep.yaml unsubscribe Substring Populated If sender matches, the unsubscribe is refused. Banks, healthcare, .gov, security senders.
kill.yaml unsubscribe, filters apply Substring Empty Forces unsubscribe + archive regardless of message-count threshold
humans.yaml filters apply Exact email Empty Star + mark important + spam-protect.
unsubbed.yaml filters apply, verify Exact email Empty Anti-resurrection — auto-archive if a previously-unsubscribed sender tries to come back. Written automatically after each successful unsubscribe.

The unsubscribe flow prefers RFC 8058 one-click POST (the standard Gmail/Apple now require for bulk senders). Falls back to GET, then mailto. Senders without any List-Unsubscribe header are skipped, not silently archived — that's a guard against accidentally archiving a real person.

See gmail_cleanup/lists/README.md for the conflict-resolution rules between the four files.


👥 Multi-account

gmail-cleanup accounts add work@company.com   --label work
gmail-cleanup accounts add personal@gmail.com --label personal
gmail-cleanup accounts list

# Run the same command across every configured account:
gmail-cleanup autopilot --all-accounts
gmail-cleanup stats     --all-accounts
gmail-cleanup verify    --all-accounts

Partial-failure semantics: if one account errors, the others still run. Failures are reported at the end. Exit code = number of failed accounts (0 = all clean).


🤖 Daily autopilot (macOS)

# Install a launchd job that runs autopilot every day at 08:00 local:
gmail-cleanup schedule install --time 08:00 --escalate

# Check it's wired up:
gmail-cleanup schedule status

# Remove it:
gmail-cleanup schedule uninstall

Linux (systemd) and Windows (Task Scheduler) integrations are on the roadmap.


🛠 Development

pip install -e ".[dev]"
pytest                # 200+ tests, <1s
ruff check .          # lint
ruff format .         # format

CI runs pytest on Python 3.11/3.12/3.13 for every push and PR.


📚 Documentation


🤝 Contributing

Issues, PRs, and discussion all welcome. Start with CONTRIBUTING.md — it covers the safety invariants you must not break.

The cardinal rule: the tool's job is to never unsubscribe you from your bank. Anything that loosens the KEEP list semantics for convenience will be rejected.


📄 License

MIT — see LICENSE.


Built by Brian Gorzelic. Safety-tested on a 7,283-email backlog before any of you used it.

Release files for gmail-inbox-cleanup 0.6.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for gmail-inbox-cleanup 0.6.1
File Size Uploaded
gmail_inbox_cleanup-0.6.1.tar.gz 72.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for gmail-inbox-cleanup 0.6.1
File Interpreter ABI Platform
gmail_inbox_cleanup-0.6.1-py3-none-any.whl Python 3 none any Details

Total release size: 120.1 kB

Release files / gmail_inbox_cleanup-0.6.1.tar.gz

Download URL gmail_inbox_cleanup-0.6.1.tar.gz
Size 72.2 kB
Tags Source
SHA-256 checksum
How to use checksums
f7bd3fbbce4bc2b78efae28d2d1d5f445fde5b9ab4442a40a5f8b13c8385b2db
BLAKE2b-256 checksum
How to use checksums
855e12dcff91637295bd53ddc35e6228ac396b73ac490c3196d78da91664e285
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release files / gmail_inbox_cleanup-0.6.1-py3-none-any.whl

Download URL gmail_inbox_cleanup-0.6.1-py3-none-any.whl
Size 47.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7ef32e405ce2180691b6e31b8c0f9a3c617503a7320344e23beb1c2495530336
BLAKE2b-256 checksum
How to use checksums
51f0069213cb8d1fd326cbfd118e2f121ae16214e680e4779ea9aab22097b738
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.6.1 This release

2 release files

0.6.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page