Skip to main content

Bulk Email Sender

PyPI version Python License: MIT

A modular, robust, and state-persisting mass email sender library for Python. It supports multiple sender accounts with round-robin rotation, personalized recipient names, automatic daily safety caps, cooldown intervals between emails, attachments, and automatic daily counter resets.


Quick Start

pip install bulk-email-sender

Usage

CLI

# Run the campaign (reads config.json from the current directory)
bulk-email-sender

# Reset progress state and start over
bulk-email-sender --reset

# Or run as a module
python -m bulk_email_sender [--reset]

Programmatic

The library never configures logging handlers — it only emits log records via the standard logging module. Set up your own handlers before calling run():

import logging
from bulk_email_sender import BulkEmailSender

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    handlers=[
        logging.FileHandler("/var/log/campaign.log", encoding="utf-8"),
        logging.StreamHandler(),
    ],
)

body = """I hope this message finds you well.

I am writing to express my interest in potential job opportunities in IT.
Please find attached my curriculum vitae for your consideration.

Thank you for your time and review.
"""

config = {
    "provider": "gmail",
    "senders": [
        {
            "email": "you@gmail.com",
            "password": "your-app-password",
            "name": "Name",
        },
        {
            "email": "you@yahoo.com",
            "password": "your-app-password-2",
            "name": "Name",
            "provider": "yahoo",
        },
    ],
    "leads_path": "leads.txt",          # .txt or .csv
    "subject": "Your Subject",
    "body": body,
    "attachment_path": "resume.pdf",    # optional, leave "" for none
    "interval_seconds": 60,
    "daily_limit": 450,
    "state_file_path": "/var/log/campaign.json",  # optional, defaults to ./email_state.json
}

BulkEmailSender(config).run()

Note: Environment variables GMAIL_SENDER and GMAIL_APP_PASSWORD override the "senders" list at runtime — useful for CI/containerised deployments.


Configuration Reference

Key Type Description
provider string Default SMTP provider for all senders. See providers.
senders array List of sender accounts. At least one is required.
senders[].email string Sender email address.
senders[].password string App password (not your login password).
senders[].name string Display name shown to recipients.
senders[].provider string (optional) Overrides the global provider for this account.
leads_path string Path to the recipients file (.txt or .csv).
subject string Email subject line.
body string Required. The email body as a plain string. Line breaks (\n) are automatically converted to <br> in HTML.
attachment_path string (optional) Path to a file to attach. Leave "" for none.
interval_seconds number Seconds to wait between emails.
daily_limit number Maximum emails to send per day.
log_path string (optional) Relative or absolute path for the log file. Defaults to ./email_sender.log.
state_file_path string (optional) Relative or absolute path for the state file. Defaults to ./email_state.json.

SMTP Provider Presets

Built-in presets automatically configure the host and port for the chosen "provider":

Provider Name Default Host Port
"gmail" smtp.gmail.com 587
"yahoo" smtp.mail.yahoo.com 587
"icloud" smtp.mail.me.com 587
"ZOHO" smtp.zoho.com 587
"GMX" smtp.gmx.com 587
"YANDEX" smtp.yandex.com 587
"AOL" smtp.aol.com 587

Individual senders can override the global provider by setting their own "provider", "smtp_host", or "smtp_port" keys inside the senders entry. The library will automatically reconnect with the correct credentials when the active sender changes.


Round-Robin Account Rotation

When multiple accounts are listed in "senders", the library rotates through them in round-robin order (1st email → account 1, 2nd email → account 2, 3rd email → account 1, …). If the rotation switches accounts, the SMTP connection is automatically re-established with the new credentials.



Defining Recipient Leads

Plain Text (.txt)

One recipient per line. Name is optional and can be separated by , or ;:

another-recipient@example.com
Jane Doe; jane.doe@example.com

CSV (.csv)

Requires an email column. An optional name column enables personalised greetings:

name,email
Jane,jane.doe@gmail.com
,another-recipient@example.com

When a name is present, the greeting becomes "Hello Jane,". When absent, it defaults to "Hello,".


Email Attachments (Optional)

Set "attachment_path" to a file path to attach it to every email.

  • Supported formats: Any file type (.pdf, .docx, .xlsx, .zip, …).
  • Size limit: Files larger than 25 MB will abort the campaign before it starts.
  • Missing files: If the path does not exist, the campaign is aborted with an error.
  • No attachment: Leave "attachment_path": "" or omit the key entirely.

Metadata

Release files for bulk-email-sender 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for bulk-email-sender 0.3.0
File Size Uploaded
bulk_email_sender-0.3.0.tar.gz 14.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for bulk-email-sender 0.3.0
File Interpreter ABI Platform
bulk_email_sender-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 28.8 kB

Release files / bulk_email_sender-0.3.0.tar.gz

Download URL bulk_email_sender-0.3.0.tar.gz
Size 14.2 kB
Tags Source
SHA-256 checksum
How to use checksums
ea2acf236669cdf480477f65f1ca3d40ade4028f51b4d3f7dec9373a94d240a1
BLAKE2b-256 checksum
How to use checksums
40494030a8172ebbe21f01b116327bd0b8df32137e9a147a7c133d911bdaa814
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 25, 2026.

Transparency log

Release files / bulk_email_sender-0.3.0-py3-none-any.whl

Download URL bulk_email_sender-0.3.0-py3-none-any.whl
Size 14.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9b0d2af15deaa8087d3f6720852b42d1d99040db444879384944315a0545d1c9
BLAKE2b-256 checksum
How to use checksums
bddd50de8224f42eb0c53d6d182e02939afbe2af071fd0347058a067b044ab21
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page