Automating & delivering reports
Email delivery
smtplib, Outlook and the Graph API — attachments, HTML bodies and recipient lists.
Start with the full guides
The long-form treatment of email delivery, with the reasoning the shorter pages assume.
- GuideEmailing Excel Reports with smtplibAttach and send .xlsx reports from Python using only the stdlib smtplib and email modules — with the modern EmailMessage API, TLS, and clear error handling.
- OverviewAutomating Reporting Workflows with Python and ExcelBuild an end-to-end Python reporting pipeline: ingest data, transform it with pandas, generate a styled Excel workbook, then schedule and email the result.
Step-by-step how-tos
One task each, with code you can copy, run and adapt.
- How-toAttach Multiple Excel Files to One Email in PythonSend several workbooks in one message — loop the attachments with the right MIME type, guard the total size, zip when it grows, and verify every file made it in.
- How-toDownload Excel Attachments from an Email Inbox with PythonPick up the spreadsheets suppliers and colleagues email in, without anyone saving them by hand. Search a mailbox over IMAP, save only genuine Excel attachments under safe file names, decode international file names, skip messages already processed, and move handled mail to a folder so the job can run every few minutes.
- How-toEmail a Download Link Instead of an Excel AttachmentSend large or sensitive Excel reports as an expiring download link instead of an attachment: decide when a link is better, upload the workbook to private S3 storage, create a presigned URL that downloads with the right file name and stops working after a set time, write an email that states the expiry, and check the link before sending.
- How-toEmail an Excel Report with an HTML Summary BodyPut the headline numbers in the email itself — build an HTML body with a plain-text fallback, render a DataFrame as a styled table, and attach the workbook for the detail.
- How-toEmbed a Chart Image in the Body of a Report EmailShow the report chart right in the email instead of hiding it in an attachment: render the workbook's own Excel chart to a sharp PNG through LibreOffice and pypdfium2 (or draw it with matplotlib), size it for phones and high-DPI screens, keep it light and dark-mode safe, embed several images by Content-ID, and test that every image reference resolves.
- How-toPersonalise Report Emails with Jinja2 TemplatesWrite the subject, plain-text and HTML body of report emails as Jinja2 templates instead of f-strings: one context per recipient, autoescaping for names and comments that contain HTML characters, StrictUndefined so a missing field stops the run, filters for money and percentages, conditional paragraphs, a shared layout, and previews written to disk before anything is sent.
- How-toQueue and Retry Failed Report EmailsStop losing report emails when the mail server has a bad moment: write each message to a small SQLite outbox instead of sending it inline, deliver from the outbox with per-recipient status, retry temporary SMTP failures with backoff, mark permanent rejections as failed instead of retrying them forever, and never send the same report twice when the job is rerun.
- How-toSend a Weekly Digest of Several Reports in One EmailReplace five separate report emails with one weekly digest: collect the week's report files from a manifest, pull a headline figure from each workbook, build one HTML email with a section per report and a plain-text alternative, attach the files while keeping under a size limit (linking the large ones instead), state clearly which reports were not produced, and preview the message before sending.
- How-toSend an Excel Report Through Outlook from PythonCompose and send from a real Outlook profile with win32com: attachments by absolute path, the signature preserved, shared-mailbox senders, and Display for review before sending.
- How-toSend an Excel Report with Amazon SESEmail a generated workbook through Amazon Simple Email Service from Python: build the MIME message with an HTML summary and the .xlsx attached, send it with boto3's SES v2 client, handle unverified senders, the sandbox and throttling, stay under the size limit, and test the whole flow locally with moto — no AWS account or real email needed.
- How-toSend an Excel Report with Gmail from PythonEmail an Excel workbook from a Gmail or Google Workspace account with Python's smtplib — app passwords and why your normal password is refused, port 465 versus 587, building the message with the correct xlsx MIME type, handling partly refused recipients, retrying only what is safe to retry, and XOAUTH2 when app passwords are switched off.
- How-toSend an Excel Report with the SendGrid APIEmail an Excel report through Twilio SendGrid's Web API instead of SMTP: build a Mail with HTML and text bodies, attach the workbook as base64 with the right MIME type, send one personalised email per recipient, use a dynamic template, test safely with sandbox mode, handle API errors and size limits, and check the request before it leaves.
- How-toSend Each Recipient Their Own Excel Report with PythonEmail every regional manager a workbook with only their region's figures — built in memory, sent over one SMTP connection, logged per recipient and safe to rerun. Check the recipient mapping before anything is sent, preview the plan, handle a rejected address without stopping the batch, and verify nobody received someone else's data.
- How-toSend an Excel Report to Multiple Recipients in PythonEmail one .xlsx report to many recipients in Python: To vs Cc vs Bcc, hidden distribution lists, personalized per-recipient sends over one SMTP connection.
- How-toSend Report Emails at Each Recipient's Local TimeDeliver a report email at 08:00 in each recipient's own time zone from one scheduled Python job: keep an IANA time zone per recipient in the distribution list, run the job hourly, decide who is due with zoneinfo, skip local weekends and holidays, record sends so nobody gets the report twice, and check the logic across daylight saving changes.
- How-toTest Report Emails with a Local SMTP ServerCheck what your report job actually sends — recipients, subject, body and the attached workbook — without emailing anyone. Run a throwaway SMTP server with aiosmtpd, point the job at it through configuration, capture every message, and assert on it in pytest, including the Bcc recipients that never appear in the headers.
- How-toZip Large Excel Attachments Before Emailing ThemAn .xlsx is already a zip file, so zipping it saves only a few percent — measured here at 4% on an 8.6 MB workbook. Find out what actually shrinks a report enough to email: size budgets that include base64 overhead, repacking at maximum compression, CSV inside a zip for raw data, and splitting attachments across messages.
- How-toBuild a Credit Control Dunning Letter RunRun credit control reminders from an open receivables export with pandas: decide each customer's reminder level from their most overdue invoice and their letter history, leave out disputed invoices, payment plans and small balances, never escalate faster than the policy allows, produce a run workbook for review and a letter for each customer listing what they owe, and record what was sent.
- How-toSend an Alert When an Excel Report Job FailsMake a failing Excel report job tell someone — a top-level wrapper that turns any exception into an alert with the error, host and traceback, notification channels that cannot hide the original failure, one alert per failure streak plus reminders and a recovery notice, and a heartbeat check for jobs that never ran at all.
- How-toStore Credentials for Excel Report Scripts SafelyGet database passwords, SMTP logins and API keys out of report scripts: environment variables with a clear error when one is missing, a .env file for development only, the operating system keyring on desktops, a cloud secret manager for servers, identities that need no secret at all, and logs that never print a password.
Follow it through a track
Prefer an ordered route? These tracks cover email delivery along the way, in the order that builds on itself.
Related topics
Subjects that keep turning up on the same pages as this one, most overlap first.
Email delivery is part of Automating & delivering reports.