Skip to content

Email activity — sends, receives, and reads per day.

The Exchange Online activity report is the pulse of mail usage: how many messages were sent, received and read each day over the reporting period. Use it to spot usage trends and to justify (or reclaim) mail licensing.

python activity.py
python activity.py --period D90

Permissions

Requires application permission Reports.Read.All (delegated works too).

Reference

View source

from __future__ import annotations

import argparse

from office365.graph_client import GraphClient
from tests.settings import client_id, client_secret, tenant


def main() -> None:
    parser = argparse.ArgumentParser(description="Email activity (send / receive / read)")
    parser.add_argument("--period", default="D30", help="Aggregation period: D7/D30/D90/D180 (default: D30)")
    args = parser.parse_args()

    client = GraphClient(tenant=tenant).with_client_secret(client_id, client_secret)
    rows = client.reports.get_email_activity_counts(args.period).execute_query().csv_rows()

    print(f"Email activity ({args.period}) — {len(rows)} day(s)\n")
    print(f"{'Date':12s}  {'Sent':>8s}  {'Received':>9s}  {'Read':>8s}")
    totals = {"Send": 0, "Receive": 0, "Read": 0}
    for row in sorted(rows, key=lambda r: r.get("Report Date") or ""):
        for key in totals:
            totals[key] += int(row.get(key) or 0)
        print(
            f"{(row.get('Report Date') or '?')[:10]:12s}"
            f"  {int(row.get('Send') or 0):>8,}"
            f"  {int(row.get('Receive') or 0):>9,}"
            f"  {int(row.get('Read') or 0):>8,}"
        )

    print(f"\nTotals: {totals['Send']:,} sent, {totals['Receive']:,} received, {totals['Read']:,} read")


if __name__ == "__main__":
    main()

← Back to Mail reports