Skip to main content

PappyMail SDK for Python

Official Python SDK for PappyMail - A powerful email platform combining transactional email, campaigns, and inbox capabilities.

Installation

pip install pappymail

Quick Start

import asyncio
from pappymail import PappyMail

async def main():
    client = PappyMail("pm_live_your_api_key")
    
    response = await client.send_simple(
        from_email="noreply@yourdomain.com",
        to="user@example.com",
        subject="Hello from PappyMail!",
        html="<h1>Welcome!</h1><p>Thanks for signing up.</p>",
    )
    
    print(f"Message ID: {response.message_id}")
    
    await client.close()

asyncio.run(main())

Or use as context manager:

async with PappyMail("pm_live_your_api_key") as client:
    response = await client.send_simple(
        from_email="noreply@yourdomain.com",
        to="user@example.com",
        subject="Hello!",
        html="<h1>Welcome!</h1>",
    )

Features

Send Transactional Emails

from pappymail import SendEmailRequest, EmailAddress, TrackingSettings

response = await client.mail.send(SendEmailRequest(
    **{
        "from": EmailAddress(email="noreply@yourdomain.com", name="Your App"),
        "to": [EmailAddress(email="user@example.com", name="John Doe")],
        "subject": "Order Confirmation",
        "html_content": "<h1>Order #12345</h1><p>Thank you!</p>",
        "text_content": "Order #12345 - Thank you!",
        "tracking": TrackingSettings(track_opens=True, track_clicks=True),
    }
))

Send with Templates

response = await client.mail.send(SendEmailRequest(
    **{
        "from": EmailAddress(email="noreply@yourdomain.com"),
        "to": [EmailAddress(email="user@example.com")],
        "template_id": "tmpl_welcome_email",
        "template_data": {
            "first_name": "John",
            "order_id": "12345",
        },
    }
))

Send Bulk Personalized Emails

from pappymail import Personalization

response = await client.mail.send(SendEmailRequest(
    **{
        "from": EmailAddress(email="noreply@yourdomain.com"),
        "subject": "Hello {{name}}!",
        "html_content": "<p>Hi {{name}}, welcome!</p>",
        "personalizations": [
            Personalization(
                to=[EmailAddress(email="alice@example.com")],
                substitutions={"name": "Alice"},
            ),
            Personalization(
                to=[EmailAddress(email="bob@example.com")],
                substitutions={"name": "Bob"},
            ),
        ],
    }
))

With Attachments

from pappymail import Attachment

response = await client.mail.send(SendEmailRequest(
    **{
        "from": EmailAddress(email="noreply@yourdomain.com"),
        "to": [EmailAddress(email="user@example.com")],
        "subject": "Your Invoice",
        "html_content": "<p>Please find your invoice attached.</p>",
        "attachments": [
            Attachment.from_file("/path/to/invoice.pdf"),
            Attachment.from_bytes("data.csv", csv_bytes, "text/csv"),
        ],
    }
))

Manage Email Templates

from pappymail import CreateTemplateRequest, UpdateTemplateRequest

# Create template
template = await client.templates.create(CreateTemplateRequest(
    name="Welcome Email",
    subject="Welcome, {{first_name}}!",
    html_content="<h1>Welcome, {{first_name}}!</h1>",
))

# List templates
templates, pagination = await client.templates.list()

# Update template
await client.templates.update(template.id, UpdateTemplateRequest(
    html_content="<h1>Welcome, {{first_name}}!</h1><p>We're excited!</p>",
))

# Delete template
await client.templates.delete(template.id)

Email Campaigns

from pappymail import CreateCampaignRequest

# Create campaign
campaign = await client.campaigns.create(CreateCampaignRequest(
    name="Summer Sale",
    subject="Don't miss our Summer Sale!",
    **{
        "from": EmailAddress(email="marketing@yourdomain.com", name="Your Store"),
        "contact_list_id": "list_abc123",
        "html_content": "<h1>Summer Sale!</h1><p>Up to 50% off!</p>",
        "track_opens": True,
        "track_clicks": True,
    }
))

# Send campaign
result = await client.campaigns.send(campaign.id)

# Or schedule for later
from datetime import datetime, timedelta
await client.campaigns.schedule(campaign.id, datetime.utcnow() + timedelta(days=1))

# Get campaign stats
stats = await client.campaigns.get_stats(campaign.id)
print(f"Opens: {stats.opened}, Clicks: {stats.clicked}")

Contact Management

from pappymail import CreateContactListRequest, CreateContactRequest, UpsertContactRequest

# Create contact list
contact_list = await client.contacts.create_list(CreateContactListRequest(
    name="Newsletter Subscribers",
    description="Users subscribed to our newsletter",
))

# Add contact
contact = await client.contacts.add(contact_list.id, CreateContactRequest(
    email="user@example.com",
    first_name="John",
    last_name="Doe",
    custom_fields={"plan": "premium"},
))

# Import contacts
import_result = await client.contacts.import_contacts(contact_list.id, [
    CreateContactRequest(email="alice@example.com", first_name="Alice"),
    CreateContactRequest(email="bob@example.com", first_name="Bob"),
])

# Upsert contact
await client.contacts.upsert(UpsertContactRequest(
    email="user@example.com",
    list_id=contact_list.id,
    first_name="John Updated",
))

Domain Management

# Add domain
domain = await client.domains.add("yourdomain.com")

# Get DNS records to configure
for record in domain.dns_records or []:
    print(f"{record.type}: {record.name} -> {record.value}")

# Verify domain
verification = await client.domains.verify(domain.id)
if verification.is_verified:
    print("Domain verified successfully!")

Webhooks

# Create webhook
webhook = await client.webhooks.create(
    "https://yourapp.com/webhooks/email",
    ["delivered", "opened", "clicked", "bounced"],
)

# List webhooks
webhooks = await client.webhooks.list()

# Delete webhook
await client.webhooks.delete(webhook.id)

# Verify webhook signature (in your Flask/FastAPI endpoint)
from pappymail import PappyMail

@app.post("/webhooks/email")
async def handle_webhook(request: Request):
    payload = await request.body()
    signature = request.headers.get("X-PappyMail-Signature", "")
    
    if not PappyMail.verify_webhook_signature(payload.decode(), signature, "your_secret"):
        raise HTTPException(status_code=401)
    
    # Process webhook...
    return {"status": "ok"}

Analytics

from datetime import datetime, timedelta

# Get stats for date range
stats = await client.stats.get(
    start_date=datetime.utcnow() - timedelta(days=30),
    end_date=datetime.utcnow(),
    aggregation="day",
)

for day in stats:
    print(f"{day.date}: Sent={day.sent}, Opened={day.opened}")

# Get global account stats
global_stats = await client.stats.get_global()
print(f"Daily usage: {global_stats.daily_usage}/{global_stats.daily_limit}")

Configuration

Full Options

from pappymail import PappyMail, PappyMailOptions

client = PappyMail(PappyMailOptions(
    api_key="pm_live_your_api_key",
    base_url="https://pappymall.com/api/v1",  # default
    timeout=30.0,  # seconds
    max_retries=3,  # default
    enable_retry=True,  # default
))

Sandbox Environment

client = PappyMail(PappyMailOptions(
    api_key="pm_test_your_api_key",
    base_url="https://pappymall.com/api/v1/sandbox",
))

Error Handling

from pappymail import (
    PappyMailError,
    ValidationError,
    RateLimitError,
    UnauthorizedError,
)

try:
    response = await client.mail.send(request)
except ValidationError as e:
    print(f"Validation error: {e.message}")
    for detail in e.details:
        print(f"  - {detail.field}: {detail.message}")
except RateLimitError as e:
    print(f"Rate limited. Retry after: {e.retry_after} seconds")
except UnauthorizedError:
    print("Invalid API key")
except PappyMailError as e:
    print(f"Error: {e.code} - {e.message}")

Requirements

  • Python 3.8+
  • httpx
  • pydantic

License

MIT License - see LICENSE file for details.

Support

Metadata

Release files for pappymail 1.0.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 pappymail 1.0.0
File Size Uploaded Provenance
pappymail-1.0.0.tar.gz 13.0 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pappymail 1.0.0
File Interpreter ABI Platform
pappymail-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 31.1 kB

Release files / pappymail-1.0.0.tar.gz

Download URL pappymail-1.0.0.tar.gz
Size 13.0 kB
Tags Source
SHA-256 checksum
How to use checksums
524cae015936a49dd7dbf3de0ced48d6671a8a96621e2a4e5384b58650840826
BLAKE2b-256 checksum
How to use checksums
5e95dc7962b633d82c870c70d806f196c1f6875c7c2a28fb1bc903bb2143ea4c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.6

Release files / pappymail-1.0.0-py3-none-any.whl

Download URL pappymail-1.0.0-py3-none-any.whl
Size 18.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f44c5daef1ab1d34c448f1ddda1aafdcc11531e304b00f99d0bd45b964288c68
BLAKE2b-256 checksum
How to use checksums
bbb9f6ef5ea5231a937c883488b665578302b871a7215e8324e46b6cc0682570
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.6

Security for pappymail 1.0.0

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files No provenance

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