Official Python SDK for PappyMail - Send transactional emails, manage campaigns, and build email experiences.
Project description
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
- Documentation: https://pappymall.com/Public/MailApi
- Contact: https://pappymall.com/Public/Contact
- Email: support@pappymall.com
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
pappymail-1.0.0.tar.gz
(13.0 kB
view details)
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
pappymail-1.0.0-py3-none-any.whl
(18.1 kB
view details)
File details
Details for the file pappymail-1.0.0.tar.gz.
File metadata
- Download URL: pappymail-1.0.0.tar.gz
- Upload date:
- Size: 13.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
524cae015936a49dd7dbf3de0ced48d6671a8a96621e2a4e5384b58650840826
|
|
| MD5 |
077c347f5b7bd3565f634a4347ae7606
|
|
| BLAKE2b-256 |
5e95dc7962b633d82c870c70d806f196c1f6875c7c2a28fb1bc903bb2143ea4c
|
File details
Details for the file pappymail-1.0.0-py3-none-any.whl.
File metadata
- Download URL: pappymail-1.0.0-py3-none-any.whl
- Upload date:
- Size: 18.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f44c5daef1ab1d34c448f1ddda1aafdcc11531e304b00f99d0bd45b964288c68
|
|
| MD5 |
9b1bf0d030e09c8aaff5769bb634c81e
|
|
| BLAKE2b-256 |
bbb9f6ef5ea5231a937c883488b665578302b871a7215e8324e46b6cc0682570
|