Automate Your Inbox: Python Script to Organize Gmail with the API
Is your inbox overflowing with updates, promotions, and unimportant emails? With a little Python and the Gmail API, you can automate email organization tasks like applying labels, archiving, or forwarding messages based on specific criteria. In this guide, we’ll walk through building a real Python script to organize your inbox so you can focus on what matters.
1. Setting Up Access: Gmail API and OAuth 2.0
To access your Gmail inbox using Python, we first need to enable the Gmail API and authorize our script using OAuth 2.0.
Steps:
- Go to the Google Cloud Console and create a new project.
- Enable the Gmail API under APIs & Services.
- Under “Credentials”, create an OAuth 2.0 Client ID for a Desktop application.
- Download the
credentials.jsonfile to your working directory.
Install the necessary libraries:
pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
Authenticate and build a service object:
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
import os.path
SCOPES = ['https://www.googleapis.com/auth/gmail.modify']
def get_gmail_service():
creds = None
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
with open('token.json', 'w') as token:
token.write(creds.to_json())
return build('gmail', 'v1', credentials=creds)
2. Fetching Emails Programmatically
Once authenticated, you can now retrieve emails based on criteria like unread status, sender, or keywords. Gmail uses a powerful query syntax similar to what you use in the search bar.
def list_messages(service, query='is:unread'):
results = service.users().messages().list(userId='me', q=query).execute()
messages = results.get('messages', [])
return messages
service = get_gmail_service()
unread_messages = list_messages(service, 'is:unread label:inbox')
print(f"Found {len(unread_messages)} unread emails")
Tip: You can also filter by from:, subject:, has:attachment, or time (e.g., newer_than:2d).
3. Applying Labels, Archiving or Forwarding
Now let’s automate some basic actions depending on email content. For example, label all GitHub notification emails or move them to Archives.
def modify_email(service, msg_id, add_labels=[], remove_labels=[]):
msg_labels = {'addLabelIds': add_labels, 'removeLabelIds': remove_labels}
service.users().messages().modify(userId='me', id=msg_id, body=msg_labels).execute()
Example: Archive a message
for msg in unread_messages:
msg_id = msg['id']
modify_email(service, msg_id, remove_labels=['INBOX'])
Example: Add a ‘GitHub’ label
def get_label_id(service, label_name):
labels = service.users().labels().list(userId='me').execute().get('labels', [])
for label in labels:
if label['name'] == label_name:
return label['id']
return None
label_id = get_label_id(service, 'GitHub')
for msg in unread_messages:
message = service.users().messages().get(userId='me', id=msg['id']).execute()
if 'github.com' in str(message.get('snippet', '')).lower():
modify_email(service, msg['id'], add_labels=[label_id], remove_labels=['INBOX'])
4. Putting It All Together: Rule-Based Automation Engine
Let’s now generalize the above into a simple rule engine. You can define rules in a list and let the script process them one by one.
rules = [
{
'query': 'from:noreply@github.com is:unread',
'label': 'GitHub',
'archive': True
},
{
'query': 'from:newsletter@producthunt.com newest',
'label': 'Newsletters',
'archive': False
}
]
for rule in rules:
messages = list_messages(service, rule['query'])
label_id = get_label_id(service, rule['label'])
for msg in messages:
modify_email(service, msg['id'],
add_labels=[label_id] if label_id else [],
remove_labels=['INBOX'] if rule['archive'] else [])
This modular approach allows you to easily extend or modify rules without editing core logic.
5. Optimization and Going Beyond
Performance Tips:
- Use batch requests (with
batch_http_request) when processing large amounts of messages. - Cache label IDs to avoid repeated API requests.
- Log actions to a local file or database to track automation activity.
Going further:
- Automatically forward invoices to your accounting system.
- Set up reminders for unread emails after 3 days.
- Integrate with Slack to notify you of urgent emails.
Here’s a batch labeling example with simple batching logic:
from googleapiclient.http import BatchHttpRequest
def process_in_batch(service, message_ids, label_ids):
def callback(request_id, response, exception):
if exception:
print(f"Error for message {request_id}: {exception}")
else:
print(f"Successfully updated message {request_id}")
batch = BatchHttpRequest(callback=callback)
for msg_id in message_ids:
batch.add(service.users().messages().modify(userId='me', id=msg_id,
body={'addLabelIds': label_ids}))
batch.execute()
Conclusion
Automating your inbox can drastically reduce repetitive tasks and help you stay focused on critical messages. With the Gmail API and Python, applying custom rules is not only powerful but also fun once scripted. Whether labeling GitHub notifications or archiving daily newsletters, you’re now equipped to build a smarter inbox.
Happy scripting!
Useful links:


