AutoUnsub
A python tool that scans your Gmail inbox for senders with unsubscribe options and lets you unsubscribe from them without opening a bunch of emails yourself.
Overview
I made AutoUnsub because my inbox was getting kinda stupid with newsletters, random notifications, and stuff I signed up for once and completely forgot about.
Unsubscribing isn't really hard, but doing it over and over again is annoying, so I wanted to automate that part.
It uses Gmail IMAP to scan message headers and a small parser to figure out what unsubscribe option an email has. SQLite keeps track of the senders and messages so the same stuff doesn't keep getting counted.
I also didn't want to put everything into one massive Python file, so main.py is the only thing you actually run and everything else is split into smaller files.
Features
| Feature | |
|---|---|
| Scan Gmail from however many days back you want | |
| Set a max number of messages to scan | |
| Detect one-click unsubscribe options | |
| Detect normal HTTP and HTTPS unsubscribe links | |
Detect mailto: unsubscribe links |
|
| Store senders and message data in SQLite | |
| Don't count the same message twice | |
| Show message counts for each sender | |
| Show unsubscribe methods | |
| Unsubscribe from one sender | |
| Unsubscribe from multiple senders | |
| Track successful and failed attempts | |
| Store the Gmail app password using the system keyring | |
| Colored terminal interface |
How It Works
Gmail -> IMAP -> parser -> SQLite -> terminal -> unsubscribe
The scanner only gets the headers it actually needs instead of downloading the whole email.
Then it pulls out the sender, date, and unsubscribe info and sends that into the parser.
The results get stored in SQLite so AutoUnsub remembers what it has already seen.
When you unsubscribe, it uses whatever method was found in the email.
One-click options use a POST request, normal links use an HTTP request, and mailto: options get turned into an email and sent through Gmail SMTP.
Project Structure
| Path | Contents |
|---|---|
main.py |
the thing you actually run |
sweeper/config.py |
handles Gmail credentials |
sweeper/db.py |
DB |
sweeper/imap_scan.py |
scans Gmail |
sweeper/sweep.py |
connects the scanner, parser and database |
sweeper/unsub_parse.py |
figures out unsubscribe methods |
sweeper/unsubscribe.py |
actually sends the unsubscribe request |
tests/ |
tests for everything |
requirements.txt |
Python dependencies |
README.md |
this file |
Install
From PyPI
Once released, the normal user install will be:
python -m pip install auto-unsub
auto-unsub
This installs AutoUnsub as a real command-line application, so users do not need to clone the repository.
From source
For development:
python -m pip install -e .
auto-unsub
Development setup
It is recommended to use a virtual environment so the dependencies don't pollute your global Python installation:
python -m venv .venv
source .venv/bin/activate
On Windows:
python -m venv .venv
.venv\Scripts\activate
Then install the project:
python -m pip install -e .
Using AutoUnsub
On the first run it asks for your Gmail address and app password.
The app password gets stored using the system keyring so you don't have to enter it every time.
You do have to create the app password yourself from your Google account first.
After that you choose how many days back you want to scan and how many messages you want to check.
Then it shows you what it found.
What I Learned
Honestly this taught me way more than I expected.
I learned how Gmail IMAP works, how email headers are structured, and how unsubscribe headers can have a bunch of different formats.
I also got way more comfortable with SQLite and keeping state between runs.
The networking part was kinda interesting too because fetching messages one by one was ridiculously slow, so I changed it to fetch them in batches (default 25, but I can change this).
Real email data was also way messier than the fake tests I started with because some emails have no unsubscribe options at all and some have huge weird unsubscribe URLs.
Why I Built It
Mostly because I wanted it for myself.
My inbox has a bunch of random stuff that I don't really care about anymore, and manually cleaning it up is just boring and repetitive.
Current Status
The main thing works now.
AutoUnsub can connect to Gmail, scan real messages, figure out which ones have unsubscribe options, save everything to SQLite, and actually send unsubscribe requests.
The interface is still pretty basic and there's still a lot I want to improve, mainly making the scanning less slow, handling weird unsubscribe services better, and making the whole thing feel like an actual finished app.
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 auto_unsub-1.0.0.tar.gz.
File metadata
- Download URL: auto_unsub-1.0.0.tar.gz
- Upload date:
- Size: 13.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
38522457cff275f04bbf0af3a3eb9016adbed350717739b5b7988074b35c6e32
|
|
| MD5 |
b7948a8717f0955667389ac51fbef3af
|
|
| BLAKE2b-256 |
875886623acbad3581f06da5227649eda8d25cc5086e7e3472e0087d2cdab762
|
Provenance
The following attestation bundles were made for auto_unsub-1.0.0.tar.gz:
Publisher:
publish.yml on Nithish1201-code/AutoUnsub
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
auto_unsub-1.0.0.tar.gz -
Subject digest:
38522457cff275f04bbf0af3a3eb9016adbed350717739b5b7988074b35c6e32 - Sigstore transparency entry: 2740460706
- Sigstore integration time:
-
Permalink:
Nithish1201-code/AutoUnsub@0dfbe967469f9511bb84655b59518c7f3e45240a -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/Nithish1201-code
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0dfbe967469f9511bb84655b59518c7f3e45240a -
Trigger Event:
release
-
Statement type:
File details
Details for the file auto_unsub-1.0.0-py3-none-any.whl.
File metadata
- Download URL: auto_unsub-1.0.0-py3-none-any.whl
- Upload date:
- Size: 13.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cd7e887224eed05cc5cfe5cc6911c084f62bc012b1ae28b860941852dffd9bb8
|
|
| MD5 |
c1e321c49f353f53ee8fd39a8bb60aeb
|
|
| BLAKE2b-256 |
aacfd45d531058f6ed8383a942e6d53001d782c04c73aab67f7f15e220a0d45e
|
Provenance
The following attestation bundles were made for auto_unsub-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on Nithish1201-code/AutoUnsub
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
auto_unsub-1.0.0-py3-none-any.whl -
Subject digest:
cd7e887224eed05cc5cfe5cc6911c084f62bc012b1ae28b860941852dffd9bb8 - Sigstore transparency entry: 2740460729
- Sigstore integration time:
-
Permalink:
Nithish1201-code/AutoUnsub@0dfbe967469f9511bb84655b59518c7f3e45240a -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/Nithish1201-code
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@0dfbe967469f9511bb84655b59518c7f3e45240a -
Trigger Event:
release
-
Statement type: