Skip to main content

sendgrid-email-service

PyPI version Python Versions License: MIT Type Checked: typed

A modern, production-ready Python SDK for sending transactional and marketing emails via SMTP (including SendGrid SMTP relays) with built-in Jinja2 templating, structured logging, secure credential handling, and comprehensive exception management.


Features

  • 🚀 Production-Grade Delivery: Reliable SMTP transport supporting TLS encryption, authentication, and timeouts.
  • 🎨 Built-in & Custom Jinja2 Templates: Comes with responsive pre-built templates (welcome, reset_password, verify_account) and allows custom template directories.
  • 🪵 Enterprise Logging: PEP 282 compliant logging with NullHandler by default, granular log levels (DEBUG, INFO, ERROR), and automatic password masking.
  • 🛡️ Comprehensive Exception Hierarchy: Clear, actionable exceptions for validation, templates, authentication, connection timeouts, and delivery errors.
  • ⚙️ Flexible Configuration: Seamlessly configure via .env files, environment variables, or typed EmailConfig objects.
  • 📦 PEP 561 Compliant: Fully typed (py.typed) for autocomplete and static analysis with MyPy and IDEs.

Installation

Install via pip:

pip install sendgrid-email-service

Quick Start

1. Environment Configuration

Create a .env file in your project root or set environment variables:

# SMTP Configuration (SendGrid, Mailpit, Amazon SES, or custom SMTP)
EMAIL_SMTP_HOST=smtp.sendgrid.net
EMAIL_SMTP_PORT=587
EMAIL_SMTP_USERNAME=apikey
EMAIL_SMTP_PASSWORD=your-sendgrid-api-key

# Sender Defaults
EMAIL_FROM=notifications@yourdomain.com
EMAIL_FROM_NAME=Your Company

# Optional Settings
EMAIL_USE_TLS=true
EMAIL_TIMEOUT=30

2. Send Plain Text or HTML Email

from sendgrid_email_service import EmailClient

# Automatically loads configuration from .env / environment variables
client = EmailClient()

# Send a simple email
client.send(
    to="recipient@example.com",
    subject="Welcome to Our Platform",
    body="Hello! Thank you for joining us.",
)

Working with Templates

sendgrid-email-service includes responsive, production-ready HTML templates out of the box.

Built-in Templates

Template Name Description Available Context Variables
welcome Onboarding & welcome message name, company_name, action_url, year
reset_password Password recovery with secure action button name, reset_url, company_name, expiry_hours, support_email, year
verify_account Email verification & activation link name, verification_url, company_name, expiry_hours, support_email, year

Example: Sending a Template Email

from sendgrid_email_service import EmailClient

client = EmailClient()

client.send(
    to="user@example.com",
    subject="Welcome to FireCompass!",
    template="welcome",
    data={
        "name": "Jane Doe",
        "company_name": "FireCompass",
        "action_url": "https://app.firecompass.com/dashboard",
        "year": 2026,
    },
)

Example: Using Custom Templates

You can point EmailClient to your own Jinja2 template directory:

from pathlib import Path
from sendgrid_email_service import EmailClient

client = EmailClient(template_directory=Path("./my_custom_templates"))

client.send(
    to="customer@example.com",
    subject="Your Invoice is Ready",
    template="monthly_invoice",  # Looks for monthly_invoice.html in ./my_custom_templates
    data={
        "customer_name": "Acme Corp",
        "invoice_number": "INV-2026-001",
        "amount_due": "$149.00",
    },
)

Programmatic Configuration

Instead of environment variables, you can configure the client directly using EmailConfig:

from sendgrid_email_service import EmailClient, EmailConfig

config = EmailConfig(
    smtp_host="smtp.sendgrid.net",
    smtp_port=587,
    smtp_username="apikey",
    smtp_password="your-api-key-here",
    from_email="no-reply@company.com",
    from_name="My Company",
    use_tls=True,
    timeout=30,
)

client = EmailClient(config=config)

Exception Handling

The SDK provides a clean exception hierarchy inheriting from EmailError:

EmailError (Base)
├── EmailConfigurationError
├── EmailValidationError
├── EmailTemplateError
└── EmailSendError
    ├── EmailConnectionError
    ├── EmailAuthenticationError
    ├── EmailTimeoutError
    └── EmailRecipientsRefusedError

Example: Robust Error Handling

import logging
from sendgrid_email_service import (
    EmailClient,
    EmailAuthenticationError,
    EmailConnectionError,
    EmailRecipientsRefusedError,
    EmailTimeoutError,
    EmailValidationError,
    EmailTemplateError,
    EmailSendError,
    EmailError,
)

client = EmailClient()

try:
    client.send(
        to="client@example.com",
        subject="Important Update",
        template="welcome",
        data={"name": "Alice", "company_name": "Acme Inc."},
    )

except EmailValidationError as e:
    print(f"Invalid email input on field '{e.field}': {e}")

except EmailTemplateError as e:
    print(f"Template error ({e.template_name}): {e}")

except EmailAuthenticationError as e:
    print(f"SMTP Auth failure for user '{e.username}': {e}")

except EmailConnectionError as e:
    print(f"Could not connect to SMTP server ({e.host}:{e.port}): {e}")

except EmailTimeoutError as e:
    print(f"Operation timed out after {e.timeout}s: {e}")

except EmailRecipientsRefusedError as e:
    print(f"Server refused recipients {e.recipients}: {e}")

except EmailSendError as e:
    print(f"Failed to send email: {e}")

except EmailError as e:
    print(f"General email SDK error: {e}")

Logging Configuration

sendgrid-email-service adheres to Python library best practices (PEP 282). By default, it emits no logs unless your application configures logging.

Example: Enabling Logs in Your Application

import logging
import sys

# Configure root or package logger
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
    handlers=[logging.StreamHandler(sys.stdout)],
)

# Enable DEBUG logging specifically for sendgrid_email_service
logging.getLogger("sendgrid_email_service").setLevel(logging.DEBUG)

Security Guarantee: Passwords and sensitive credentials are automatically masked (e.g. ***) and are never written to log files.


Local Development with Mailpit

You can test email sending locally using Mailpit included via Docker Compose:

  1. Start Mailpit:
    docker compose up -d
    
  2. Set .env to point to localhost:
    EMAIL_SMTP_HOST=localhost
    EMAIL_SMTP_PORT=1025
    EMAIL_USE_TLS=false
    
  3. View sent emails in your browser at http://localhost:8025.

Running Tests

Install dev dependencies and run pytest:

pip install -e ".[dev]"
pytest tests/ -v

Publishing to PyPI

  1. Build distribution archives:

    python -m build
    
  2. Verify archives with Twine:

    twine check dist/*
    
  3. Upload to TestPyPI (Optional):

    twine upload --repository testpypi dist/*
    
  4. Upload to PyPI:

    twine upload dist/*
    

License

Distributed under the MIT License.

sendgrid-stmp-email-service

Release files for sendgrid-email-service 0.0.1

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

Source distribution (sdist)

Source distribution for sendgrid-email-service 0.0.1
File Size Uploaded
sendgrid_email_service-0.0.1.tar.gz 22.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sendgrid-email-service 0.0.1
File Interpreter ABI Platform
sendgrid_email_service-0.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 41.8 kB

Release files / sendgrid_email_service-0.0.1.tar.gz

Download URL sendgrid_email_service-0.0.1.tar.gz
Size 22.0 kB
Tags Source
SHA-256 checksum
How to use checksums
6b073976904e178a21cf56bb4886b2e2df356d96635d3b95f1d48338641298c9
BLAKE2b-256 checksum
How to use checksums
4f17eea3543bb56252b726af9f61c7d7b8d6853ec7db4ef65f547a3e88ae08f4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.6

Release files / sendgrid_email_service-0.0.1-py3-none-any.whl

Download URL sendgrid_email_service-0.0.1-py3-none-any.whl
Size 19.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
712beb576c155025af3b9025b2d1057c5f2ed9ba4bf5d7870d582f83d0bafb28
BLAKE2b-256 checksum
How to use checksums
6ffcf9a9f5cbfa884258d013c2dcd7a9d9a5645acd5f730414273ca76c1c3980
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.6

Release history Release notifications | RSS feed

0.0.3

2 release files

0.0.2

2 release files

This release

0.0.1 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