Build a Phishing Detector with InboxWatch and Python
A complete Python script that reads .eml files, extracts email headers, and calls the InboxWatch API for threat analysis. Under 30 lines of code.
What you will build
A Python command-line tool that takes an .eml file as input, extracts the security-relevant headers, sends them to the InboxWatch analysis API, and prints a structured threat report. The entire script is under 30 lines.
This is useful for SOC teams that want to triage suspicious emails from a quarantine folder, incident responders analyzing exported messages, or anyone building email security tooling in Python.
Prerequisites
You need Python 3.8+ and the requests library:
pip install requestsYou need an InboxWatch API key. Sign up at inboxwatch.ai/check (15 free scans, then $0.10/scan), then create a key in Settings.
Set your API key
Store your API key as an environment variable. This keeps it out of your source code:
# macOS / Linux
export INBOXWATCH_API_KEY="iw_live_your_api_key_here"
# Windows (PowerShell)
$env:INBOXWATCH_API_KEY = "iw_live_your_api_key_here"
# Windows (Command Prompt)
set INBOXWATCH_API_KEY=iw_live_your_api_key_hereThe API key is optional. Without it, you get 10 requests per minute on the anonymous tier.
The complete script
Save this as phishing_check.py:
#!/usr/bin/env python3
"""Analyze an .eml file for phishing threats using InboxWatch."""
import sys
import os
import email
import requests
API_URL = "https://inboxwatch.ai/api/mcp/analyze"
API_KEY = os.environ.get("INBOXWATCH_API_KEY", "")
def analyze_eml(path: str) -> dict:
with open(path, "r", encoding="utf-8", errors="replace") as f:
msg = email.message_from_file(f)
payload = {
"from": msg.get("From", ""),
"replyTo": msg.get("Reply-To", ""),
"subject": msg.get("Subject", ""),
"messageId": msg.get("Message-ID", ""),
"authenticationResults": msg.get("Authentication-Results", ""),
"receivedSpf": msg.get("Received-SPF", ""),
"received": msg.get_all("Received", []),
"xOriginatingIp": msg.get("X-Originating-IP", ""),
}
headers = {"Content-Type": "application/json"}
if API_KEY:
headers["Authorization"] = f"Bearer {API_KEY}"
resp = requests.post(API_URL, json=payload, headers=headers, timeout=10)
resp.raise_for_status()
return resp.json()
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python phishing_check.py <file.eml>")
sys.exit(1)
result = analyze_eml(sys.argv[1])
print(f"\nThreat Score: {result['threatScore']}/100")
print(f"Threat Level: {result['threatLevel'].upper()}")
print(f"Verdict: {result['verdict']}\n")
for ind in result.get("indicators", []):
print(f" [{ind['severity'].upper()}] {ind['description']}")
if ind.get("evidence"):
print(f" Evidence: {ind['evidence']}")
auth = result.get("authentication", {})
if auth:
print(f"\nAuthentication:")
print(f" SPF: {auth.get('spf', 'n/a')}")
print(f" DKIM: {auth.get('dkim', 'n/a')}")
print(f" DMARC: {auth.get('dmarc', 'n/a')}")Usage
Run the script against any .eml file:
python phishing_check.py suspicious_email.emlExample output for a phishing email:
Threat Score: 85/100
Threat Level: CRITICAL
Verdict: HIGH RISK: 2 critical threat indicator(s) detected.
[CRITICAL] From domain uses lookalike characters to impersonate a known domain
Evidence: g00gle.com resembles google.com
[HIGH] Reply-To address differs from From and uses a free email provider
Evidence: From: g00gle.com, Reply-To: gmail.com
Authentication:
SPF: fail
DKIM: none
DMARC: failHow it works
The script does three things:
- Parse the .eml file. Python's built-in
emailmodule reads the file and provides access to all headers. No additional parsing libraries are needed. - Extract security headers. The script pulls the From, Reply-To, Subject, Message-ID, Authentication-Results, Received-SPF, Received chain, and X-Originating-IP headers. These are the fields the InboxWatch API uses for analysis.
- Call the API. A single POST request to
https://inboxwatch.ai/api/mcp/analyzewith the extracted headers. The response includes the threat score, threat level, verdict, authentication results, and detailed indicators.
Batch processing
To scan an entire directory of .eml files, wrap the script in a loop:
import glob
for eml_path in glob.glob("quarantine/*.eml"):
result = analyze_eml(eml_path)
score = result["threatScore"]
level = result["threatLevel"]
print(f"{eml_path}: {score}/100 ({level})")
if score >= 70:
print(f" >> HIGH RISK: {result['verdict']}")Your API key supports up to 1,000 requests per minute. For batch processing, the script handles the rate limit automatically via the INBOXWATCH_API_KEYenvironment variable set earlier.
Go further: scan your entire account
This script analyzes individual email headers. For full account protection, InboxWatch scans your entire Gmail or Microsoft 365 configuration: forwarding rules, delegates, OAuth apps, sign-in patterns, and 100+ security checks that run automatically every 30 minutes.
Sign up at inboxwatch.ai/check to run your first scan. 15 scans are free, then $0.10 each via Stripe. Connect your account in 60 seconds, no credit card to start.
Extending the script
Some ideas for building on this foundation:
- Write results to a CSV file for reporting
- Send Slack notifications for emails scoring above 70
- Integrate with your SIEM by forwarding JSON results to a syslog endpoint
- Add to a CI pipeline to scan test phishing emails as part of security training
- Build a web interface with Flask or FastAPI that accepts email uploads
Error handling
The API returns standard HTTP status codes:
- 200 - Analysis successful
- 400 - Invalid input (missing
fromfield or malformed JSON) - 401 - Invalid or expired API key
- 429 - Rate limit exceeded (check
Retry-Afterheader) - 500 - Server error
The script uses resp.raise_for_status() to throw an exception on non-200 responses. In production, you should catch requests.exceptions.HTTPErrorand handle rate limiting with a backoff strategy.
Scan your entire inbox for threats
This script checks one email at a time. A full InboxWatch scan checks everything: forwarding rules, delegates, OAuth apps, sign-in activity, and 100+ security checks. 15 free scans, then $0.10 each.
Start Free Scan15 free scans. No credit card to start. $0.10/scan after.
Written by Nicholas Papadam, founder of InboxWatch. Senior Analyst with 6+ years in enterprise security operations.