Skip to content

cr0hn/python-acumbamail

Acumbamail SDK for Python

Python 3.11+ License: MIT PyPI

A modern, feature-rich Python SDK for the Acumbamail API. This library provides both synchronous and asynchronous clients for managing email campaigns, mailing lists, subscribers, and analytics.

Table of Contents

Referral program

If you sign up using this link, you'll get 15% off.

https://acumbamail.com/?refered=243965

Features

  • Dual Client Support: Both synchronous (AcumbamailClient) and asynchronous (AsyncAcumbamailClient) clients
  • Modern HTTP Client: Built on httpx for better performance and HTTP/2 support
  • Type Hints: Full type annotation support for better IDE integration
  • Comprehensive API Coverage: All major Acumbamail API endpoints supported
  • Error Handling: Robust exception handling with specific error types
  • Rate Limiting: Built-in rate limit handling with exponential backoff
  • Data Models: Structured data models for all API responses
  • Documentation: Extensive docstrings and examples

Quick Start

Installation

pip install acumbamail

Basic Usage

from acumbamail import AcumbamailClient

# Initialize the client
client = AcumbamailClient(
    auth_token='your-api-token',
    default_sender_name='Your Company',
    default_sender_email='noreply@yourcompany.com'
)

# Create a mailing list
mailing_list = client.create_list(
    name="Newsletter Subscribers",
    description="Our monthly newsletter list"
)

# Add a subscriber
subscriber = client.add_subscriber(
    email="user@example.com",
    list_id=mailing_list.id,
    fields={"name": "John Doe", "company": "Acme Corp"}
)

# Create and send a campaign
campaign = client.create_campaign(
    name="Welcome Campaign",
    subject="Welcome to our newsletter!",
    content="<h1>Welcome!</h1><p>Thank you for subscribing.</p>",
    list_ids=[mailing_list.id]
)

Postman Collection

For testing and exploring the API, you can use the included Postman collection:

  • Acumbamail.postman_collection.json: Complete collection with all API endpoints pre-configured

The collection includes:

  • Campaigns: All campaign-related endpoints (getCampaignBasicInformation, getCampaignClicks, getCampaignLinks, etc.)
  • Subscribers: All subscriber and list management endpoints (getLists, getSubscribers, getListStats, etc.)

Using the Postman Collection

  1. Import the Acumbamail.postman_collection.json file into Postman
  2. Set up your environment variables:
    • token: Your Acumbamail API token
    • basepath: https://acumbamail.com/api/1/
    • campaign_id: Your campaign ID (for campaign-specific endpoints)
    • list_id: Your list ID (for list-specific endpoints)
  3. Start testing the API endpoints directly from Postman

Configuration

Client Initialization

from acumbamail import AcumbamailClient

# Basic configuration
client = AcumbamailClient(auth_token='your-token')

# Full configuration (optional)
client = AcumbamailClient(
    auth_token='your-token',
    default_sender_name='Your Company',
    default_sender_email='noreply@yourcompany.com',
    sender_company='Your Company Inc.',
    sender_country='US'
)

Basic Usage

Mailing Lists

# Get all lists
lists = client.get_lists()
for mail_list in lists:
    print(f"{mail_list.name}: {mail_list.subscribers_count} subscribers")

# Create a new list
new_list = client.create_list(
    name="Product Updates",
    description="List for product announcements"
)

# Get list statistics
stats = client.get_list_stats(new_list.id)
print(f"Total subscribers: {stats['total_subscribers']}")

Subscribers

# Add a subscriber
subscriber = client.add_subscriber(
    email="user@example.com",
    list_id=mailing_list.id,
    fields={
        "name": "John Doe",
        "company": "Acme Corp",
        "preferences": "weekly"
    }
)

# Get all subscribers
subscribers = client.get_subscribers(mailing_list.id)
for subscriber in subscribers:
    print(f"Email: {subscriber.email}")
    print(f"Fields: {subscriber.fields}")

# Remove a subscriber
client.delete_subscriber("user@example.com", mailing_list.id)

Campaigns

# Create a campaign
campaign = client.create_campaign(
    name="Monthly Newsletter",
    subject="This month's updates",
    content="""
    <h1>Monthly Newsletter</h1>
    <p>Here are the latest updates:</p>
    <ul>
        <li>New feature released</li>
        <li>Upcoming events</li>
        <li>Special offers</li>
    </ul>
    """,
    list_ids=[mailing_list.id],
    pre_header="Stay updated with our latest news"
)

# Create a campaign with custom tracking domain
campaign_with_tracking = client.create_campaign(
    name="Branded Newsletter",
    subject="Special offer just for you!",
    content="""
    <h1>Special Offer</h1>
    <p>Check out our latest deals:</p>
    <a href="https://example.com/offer">View Offer</a>
    """,
    list_ids=[mailing_list.id],
    tracking_domain="links.midominio.com"  # Custom tracking domain
)

# Schedule a campaign
from datetime import datetime, timedelta
scheduled_time = datetime.now() + timedelta(days=1)
scheduled_campaign = client.create_campaign(
    name="Scheduled Campaign",
    subject="Scheduled message",
    content="<p>This will be sent tomorrow</p>",
    list_ids=[mailing_list.id],
    scheduled_at=scheduled_time
)

Single Emails

# Send a single email
email_id = client.send_single_email(
    to_email="customer@example.com",
    subject="Order Confirmation",
    content="<h1>Thank you for your order!</h1><p>Order #12345</p>",
    category="order_confirmation"
)

Templates

# Create an email template
template = client.create_template(
    template_name="Welcome Template",
    html_content="<h1>Welcome!</h1><p>Thank you for joining our community.</p>",
    subject="Welcome to our community!",
    custom_category="onboarding"
)

# Get all templates
templates = client.get_templates()
for template in templates:
    print(f"Template: {template.name} - ID: {template.id}")

Asynchronous Usage

import asyncio
from acumbamail import AsyncAcumbamailClient

async def main():
    # Using context manager (recommended)
    async with AsyncAcumbamailClient(
        auth_token='your-token',
        default_sender_name='Your Company',
        default_sender_email='noreply@yourcompany.com'
    ) as client:
        
        # Get lists
        lists = await client.get_lists()
        
        # Create campaign
        campaign = await client.create_campaign(
            name="Async Campaign",
            subject="Hello from async!",
            content="<p>This was sent asynchronously</p>",
            list_ids=[lists[0].id]
        )
        
        # Create campaign with custom tracking domain
        branded_campaign = await client.create_campaign(
            name="Branded Async Campaign",
            subject="Special async offer!",
            content="<p>Check out our async deals!</p>",
            list_ids=[lists[0].id],
            tracking_domain="links.midominio.com"  # Custom tracking domain
        )
        
        # Get campaign statistics
        stats = await client.get_campaign_total_information(campaign.id)
        print(f"Opened: {stats.opened}")

# Run the async function
asyncio.run(main())

Analytics and Statistics

Campaign Analytics

# Get comprehensive campaign statistics
stats = client.get_campaign_total_information(campaign_id)

print(f"Total delivered: {stats.total_delivered}")
print(f"Opened: {stats.opened}")
print(f"Unique clicks: {stats.unique_clicks}")
print(f"Hard bounces: {stats.hard_bounces}")
print(f"Unsubscribes: {stats.unsubscribes}")

# Calculate rates
if stats.total_delivered > 0:
    open_rate = (stats.opened / stats.total_delivered) * 100
    click_rate = (stats.unique_clicks / stats.total_delivered) * 100
    print(f"Open rate: {open_rate:.2f}%")
    print(f"Click rate: {click_rate:.2f}%")

Click Analysis

# Get detailed click statistics
clicks = client.get_campaign_clicks(campaign_id)

for click in clicks:
    print(f"URL: {click.url}")
    print(f"Total clicks: {click.clicks}")
    print(f"Unique clicks: {click.unique_clicks}")
    print(f"Click rate: {click.click_rate:.2%}")

Opener Analysis

# Get information about who opened the campaign
openers = client.get_campaign_openers(campaign_id)

for opener in openers:
    print(f"Email: {opener.email}")
    print(f"Opened at: {opener.opened_at}")
    print(f"Country: {opener.country}")
    print(f"Browser: {opener.browser}")
    print(f"OS: {opener.os}")

Browser and OS Statistics

# Get statistics by browser
browser_stats = client.get_campaign_openers_by_browser(campaign_id)
for browser, count in browser_stats.items():
    print(f"{browser}: {count} opens")

# Get statistics by operating system
os_stats = client.get_campaign_openers_by_os(campaign_id)
for os_name, count in os_stats.items():
    print(f"{os_name}: {count} opens")

Advanced Examples

Bulk Subscriber Management

# Add multiple subscribers efficiently
subscribers_data = [
    ("user1@example.com", {"name": "User One", "company": "Corp A"}),
    ("user2@example.com", {"name": "User Two", "company": "Corp B"}),
    ("user3@example.com", {"name": "User Three", "company": "Corp C"})
]

for email, fields in subscribers_data:
    try:
        subscriber = client.add_subscriber(
            email=email,
            list_id=mailing_list.id,
            fields=fields
        )
        print(f"Added: {subscriber.email}")
    except Exception as e:
        print(f"Failed to add {email}: {e}")

Campaign Performance Monitoring

def monitor_campaign_performance(campaign_id):
    """Monitor campaign performance and alert on issues."""
    stats = client.get_campaign_total_information(campaign_id)
    
    # Calculate key metrics
    if stats.total_delivered > 0:
        open_rate = (stats.opened / stats.total_delivered) * 100
        click_rate = (stats.unique_clicks / stats.total_delivered) * 100
        bounce_rate = (stats.hard_bounces / stats.total_delivered) * 100
        
        print(f"Campaign Performance:")
        print(f"  Open rate: {open_rate:.2f}%")
        print(f"  Click rate: {click_rate:.2f}%")
        print(f"  Bounce rate: {bounce_rate:.2f}%")
        
        # Alert on poor performance
        if open_rate < 15:
            print("⚠️  Warning: Low open rate detected")
        if bounce_rate > 5:
            print("⚠️  Warning: High bounce rate detected")
        if stats.complaints > 0:
            print("⚠️  Warning: Complaints detected")

Automated Newsletter System

from datetime import datetime, timedelta

def send_weekly_newsletter():
    """Send weekly newsletter to all active subscribers."""
    
    # Get the newsletter list
    lists = client.get_lists()
    newsletter_list = next((l for l in lists if "newsletter" in l.name.lower()), None)
    
    if not newsletter_list:
        print("Newsletter list not found")
        return
    
    # Create newsletter content
    content = """
    <h1>Weekly Newsletter</h1>
    <p>Here's what happened this week:</p>
    <ul>
        <li>New product features</li>
        <li>Upcoming events</li>
        <li>Community highlights</li>
    </ul>
    <p>Stay tuned for more updates!</p>
    """
    
    # Create and send campaign
    campaign = client.create_campaign(
        name=f"Weekly Newsletter - {datetime.now().strftime('%Y-%m-%d')}",
        subject="Your weekly update is here!",
        content=content,
        list_ids=[newsletter_list.id],
        pre_header="Your weekly dose of updates and insights"
    )
    
    print(f"Newsletter sent! Campaign ID: {campaign.id}")

A/B Testing Framework

def run_ab_test(subject_a, subject_b, content_a, content_b, list_id):
    """Run A/B test with two different subject lines and content."""
    
    # Create two campaigns
    campaign_a = client.create_campaign(
        name="A/B Test - Version A",
        subject=subject_a,
        content=content_a,
        list_ids=[list_id]
    )
    
    campaign_b = client.create_campaign(
        name="A/B Test - Version B",
        subject=subject_b,
        content=content_b,
        list_ids=[list_id]
    )
    
    print(f"Created A/B test campaigns:")
    print(f"  Version A: {campaign_a.id}")
    print(f"  Version B: {campaign_b.id}")
    
    return campaign_a.id, campaign_b.id

def analyze_ab_test(campaign_a_id, campaign_b_id):
    """Analyze A/B test results."""
    
    stats_a = client.get_campaign_total_information(campaign_a_id)
    stats_b = client.get_campaign_total_information(campaign_b_id)
    
    # Calculate performance metrics
    open_rate_a = (stats_a.opened / stats_a.total_delivered) * 100 if stats_a.total_delivered > 0 else 0
    open_rate_b = (stats_b.opened / stats_b.total_delivered) * 100 if stats_b.total_delivered > 0 else 0
    
    click_rate_a = (stats_a.unique_clicks / stats_a.total_delivered) * 100 if stats_a.total_delivered > 0 else 0
    click_rate_b = (stats_b.unique_clicks / stats_b.total_delivered) * 100 if stats_b.total_delivered > 0 else 0
    
    print("A/B Test Results:")
    print(f"Version A - Open rate: {open_rate_a:.2f}%, Click rate: {click_rate_a:.2f}%")
    print(f"Version B - Open rate: {open_rate_b:.2f}%, Click rate: {click_rate_b:.2f}%")
    
    # Determine winner
    if open_rate_b > open_rate_a:
        print("πŸŽ‰ Version B wins for open rate!")
    elif open_rate_a > open_rate_b:
        print("πŸŽ‰ Version A wins for open rate!")
    else:
        print("🀝 Tie for open rate")

API Reference

Client Classes

AcumbamailClient (Synchronous)

class AcumbamailClient:
    def __init__(
        self,
        auth_token: str,
        default_sender_name: str = None,
        default_sender_email: str = None,
        *,
        sender_company: str = None,
        sender_country: str = None
    )

AsyncAcumbamailClient (Asynchronous)

class AsyncAcumbamailClient:
    def __init__(
        self,
        auth_token: str,
        default_sender_name: str = None,
        default_sender_email: str = None,
        *,
        sender_company: str = None,
        sender_country: str = None,
        timeout: float = 30.0
    )

Core Methods

List Management

  • get_lists() - Retrieve all mailing lists
  • create_list(name, description) - Create a new mailing list
  • get_list_stats(list_id) - Get list statistics
  • get_list_fields(list_id) - Get custom fields
  • get_list_segments(list_id) - Get list segments

Subscriber Management

  • get_subscribers(list_id) - Get all subscribers
  • add_subscriber(email, list_id, fields) - Add a subscriber
  • delete_subscriber(email, list_id) - Remove a subscriber

Campaign Management

  • create_campaign(name, subject, content, list_ids, ...) - Create a campaign
  • get_campaigns(complete_json) - Get all campaigns
  • send_single_email(to_email, subject, content, ...) - Send single email
  • create_template(template_name, html_content, subject, custom_category) - Create email template
Template Management

The SDK provides methods to create and manage email templates:

  • get_templates() - Retrieve all available email templates
  • create_template(template_name, html_content, subject, custom_category) - Create a new email template

Template Parameters:

  • template_name (str): Name of the template for internal organization
  • html_content (str): HTML content of the template (can include merge tags)
  • subject (str): Default subject line for emails using this template
  • custom_category (str, optional): Category to organize templates

Example:

# Create a welcome template
template = client.create_template(
    template_name="Welcome Email",
    html_content="<h1>Welcome!</h1><p>Thank you for joining.</p>",
    subject="Welcome to our community!",
    custom_category="onboarding"
)

# Get all templates
templates = client.get_templates()
for template in templates:
    print(f"Template: {template.name} - ID: {template.id}")
Campaign Parameters

The create_campaign method supports several parameters for customization:

  • name (str): Campaign name for internal organization
  • subject (str): Email subject line
  • content (str): HTML content of the email (must include *|UNSUBSCRIBE_URL|*)
  • list_ids (List[int]): Target mailing list IDs
  • from_name (str, optional): Sender name (uses default if not specified)
  • from_email (str, optional): Sender email (uses default if not specified)
  • scheduled_at (datetime, optional): When to schedule the campaign
  • tracking_enabled (bool, optional): Enable click/open tracking (default: True)
  • tracking_domain (str, optional): Custom domain for tracking URLs (e.g., "links.midominio.com")
  • pre_header (str, optional): Preview text shown in email clients

Tracking Domain: The tracking_domain parameter allows you to customize the domain used for tracking URLs in your emails. Instead of using Acumbamail's default tracking domain, you can specify your own domain (e.g., "links.midominio.com") to maintain brand consistency and improve deliverability.

Analytics

  • get_campaign_total_information(campaign_id) - Get comprehensive stats
  • get_campaign_clicks(campaign_id) - Get click statistics
  • get_campaign_openers(campaign_id) - Get opener information
  • get_campaign_soft_bounces(campaign_id) - Get bounce information

Error Handling

The SDK provides specific exception types for different error scenarios:

from acumbamail import (
    AcumbamailError,
    AcumbamailRateLimitError,
    AcumbamailAPIError,
    AcumbamailValidationError
)

try:
    campaign = client.create_campaign(
        name="Test Campaign",
        subject="Hello",
        content="<p>Test</p>",
        list_ids=[123]
    )
except AcumbamailValidationError as e:
    print(f"Validation error: {e}")
except AcumbamailRateLimitError as e:
    print(f"Rate limit exceeded: {e}")
except AcumbamailAPIError as e:
    print(f"API error: {e}")
except AcumbamailError as e:
    print(f"General error: {e}")

Examples

You can find examples in the examples directory.

Contributing

We welcome contributions! Please see our Contributing Guide for details.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

I don't offer support for this project apart from the free time I can dedicate to it. If you find a bug, please report it in the GitHub Issues section.

GitHub Issues

[!NOTE] If you need commercial support for this project, please contact me at cr0hn<-at->cr0hn.com.

About

Acumbamail Python SDK

Topics

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Languages