How to Automate Email Classification with Python

E-Mail Klassifizierung mit Python und Machine Learning
📅 March 24, 2026 ⏱ 8 min read ✍️ Leo Voss

The average knowledge worker spends 2.5 hours per day managing email. That's over 600 hours a year — roughly a third of your working time — just reading, sorting, and routing messages. If your inbox is a constant source of anxiety, you're not alone, and you're not powerless.

Python email classification has become one of the most practical AI automation projects you can build today. Whether you're handling customer support tickets, sales inquiries, or internal requests, an AI-powered email sorting system can automatically categorize incoming messages, route them to the right person, and trigger follow-up actions — all without human intervention.

This tutorial walks you through the core approaches, the actual code patterns, and how to deploy a working solution. No machine learning PhD required.

Why Automate Email Sorting?

Before diving into code, let's be concrete about the ROI. For a small business handling 100 emails per day:

Automated email categorization eliminates all three problems simultaneously. Your support team sees only support tickets. Your sales team sees only leads. Urgent messages get flagged and escalated immediately — even at 2 AM.

Three Approaches to Python Email Classification

There's no single right way to build an email classification system. Your choice depends on volume, accuracy requirements, and how much labeled data you have.

🔤 Rule-Based Filtering

  • Keyword matching and regex
  • Zero training data needed
  • Fast and predictable
  • Breaks on edge cases
  • Best for: simple routing

🤖 LLM Classification

  • GPT-4 or Claude via API
  • Near-human accuracy
  • No training data needed
  • Per-call API cost
  • Best for: complex intents

💡 For most businesses, the sweet spot is a hybrid: rule-based pre-filtering handles obvious cases (newsletters, receipts, spam), then LLM classification handles the ambiguous 30–40% that needs real understanding.

Step-by-Step: Building an AI Email Classifier

1 Connect to Your Mailbox

Python's imaplib library lets you read emails from any IMAP-compatible mailbox — Gmail, Outlook, or a custom mail server.

import imaplib
import email
from email.header import decode_header

def connect_to_mailbox(host, username, password):
    mail = imaplib.IMAP4_SSL(host)
    mail.login(username, password)
    mail.select("INBOX")
    return mail

def fetch_unread_emails(mail):
    _, message_ids = mail.search(None, "UNSEEN")
    emails = []
    for msg_id in message_ids[0].split():
        _, msg_data = mail.fetch(msg_id, "(RFC822)")
        raw_email = msg_data[0][1]
        msg = email.message_from_bytes(raw_email)
        
        subject = decode_header(msg["Subject"])[0][0]
        if isinstance(subject, bytes):
            subject = subject.decode()
        
        body = ""
        if msg.is_multipart():
            for part in msg.walk():
                if part.get_content_type() == "text/plain":
                    body = part.get_payload(decode=True).decode()
                    break
        else:
            body = msg.get_payload(decode=True).decode()
        
        emails.append({
            "id": msg_id,
            "from": msg["From"],
            "subject": subject,
            "body": body[:500]  # first 500 chars for classification
        })
    return emails

2 Classify with an LLM

Send the email subject and body snippet to an LLM with a structured prompt. The key is asking for a JSON response — this makes downstream automation reliable.

import openai
import json

client = openai.OpenAI(api_key="your-api-key")

CATEGORIES = [
    "support_request",
    "sales_inquiry", 
    "billing_question",
    "partnership",
    "newsletter",
    "spam",
    "other"
]

def classify_email(subject: str, body: str) -> dict:
    prompt = f"""Classify this email into one of these categories: {', '.join(CATEGORIES)}

Email Subject: {subject}
Email Body (excerpt): {body}

Respond with JSON only:
{{
  "category": "category_name",
  "confidence": 0.95,
  "priority": "high|medium|low",
  "summary": "one sentence summary"
}}"""

    response = client.chat.completions.create(
        model="gpt-4o-mini",  # fast and cheap for classification
        messages=[{"role": "user", "content": prompt}],
        response_format={"type": "json_object"},
        max_tokens=150
    )
    
    return json.loads(response.choices[0].message.content)

3 Route and Act on Results

Classification is only useful if it triggers action. Connect the output to your existing tools: move emails to folders, create tickets in your helpdesk, post alerts to Slack, or add leads to your CRM.

import requests

ROUTING_RULES = {
    "support_request": {
        "folder": "Support",
        "webhook": "https://your-helpdesk.com/api/tickets",
        "slack_channel": "#support"
    },
    "sales_inquiry": {
        "folder": "Sales",
        "webhook": "https://your-crm.com/api/leads",
        "slack_channel": "#sales"
    },
    "billing_question": {
        "folder": "Billing",
        "slack_channel": "#billing"
    }
}

def route_email(mail, email_data: dict, classification: dict):
    category = classification["category"]
    priority = classification["priority"]
    
    # Move to appropriate folder
    if category in ROUTING_RULES:
        rules = ROUTING_RULES[category]
        folder = rules.get("folder", "Processed")
        mail.copy(email_data["id"], folder)
        
        # Post to Slack if high priority
        if priority == "high" and "slack_channel" in rules:
            post_slack_alert(
                channel=rules["slack_channel"],
                subject=email_data["subject"],
                sender=email_data["from"],
                summary=classification["summary"]
            )
        
        # Create ticket/lead if webhook configured
        if "webhook" in rules:
            requests.post(rules["webhook"], json={
                "source": "email",
                "subject": email_data["subject"],
                "from": email_data["from"],
                "category": category,
                "priority": priority,
                "summary": classification["summary"]
            })

def post_slack_alert(channel, subject, sender, summary):
    slack_webhook = "https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"
    requests.post(slack_webhook, json={
        "channel": channel,
        "text": f"🚨 *High Priority Email*\n*From:* {sender}\n*Subject:* {subject}\n*Summary:* {summary}"
    })

Putting It All Together: The Main Loop

import time
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def run_email_classifier():
    mail = connect_to_mailbox(
        host="imap.gmail.com",
        username="your@email.com",
        password="your-app-password"
    )
    
    logger.info("Starting email classification loop...")
    
    while True:
        try:
            emails = fetch_unread_emails(mail)
            logger.info(f"Found {len(emails)} unread emails")
            
            for email_data in emails:
                classification = classify_email(
                    email_data["subject"],
                    email_data["body"]
                )
                logger.info(f"Classified: {email_data['subject'][:50]} → {classification['category']}")
                route_email(mail, email_data, classification)
            
            time.sleep(60)  # check every minute
            
        except Exception as e:
            logger.error(f"Error: {e}")
            time.sleep(30)

if __name__ == "__main__":
    run_email_classifier()

Cost Breakdown: What Does It Actually Cost?

Using gpt-4o-mini for classification (the most cost-effective option), the math is straightforward:

At any reasonable email volume, the API cost is negligible compared to the time saved. The real cost is the one-time development effort to build and configure the system.

Production Considerations

Handling False Classifications

No classifier is perfect. Build in a confidence threshold: if confidence < 0.7, route the email to a "Needs Review" folder instead of acting automatically. Review these edge cases weekly and use them to improve your prompts.

Gmail-Specific Setup

For Gmail, you'll need to enable IMAP access and create an App Password (not your regular password). Go to Google Account → Security → 2-Step Verification → App passwords. Use imap.gmail.com on port 993.

Running It 24/7

Deploy the script on a small VPS or use a serverless function triggered on a schedule. A €5/month VPS handles this workload easily. Use systemd to keep it running and automatically restart on failure.

💡 This is exactly the kind of system we built for our MailMind project — an intelligent email triage system that reduced a client's inbox processing time by 78%. The full implementation includes custom category training, CRM integration, and a dashboard for monitoring classification accuracy.

Beyond Basic Classification: What's Next

Once the basic classification loop is running, you can extend it significantly:

If you want to go deeper on business process automation beyond email, check out our guide on automating business processes for SMBs. And if you're building a full customer-facing AI system, our RAG chatbot tutorial covers how to add a knowledge base layer on top.

Want a Custom Email Automation System?

We build production-ready email classification and routing systems tailored to your business — with CRM integrations, custom categories, and monitoring dashboards.

Visit Leo Voss Automation →