Misar IO Docs

Python SDK

Full reference for the MisarMail Python client library (sync and async).

Python SDK

Installation

pip install misarmail
# or
poetry add misarmail
# or
uv add misarmail

Requires Python 3.9+.

Setup

from misarmail import MisarMail

client = MisarMail(
    api_key="msk_YOUR_KEY",          # or set MISARMAIL_API_KEY env var
    base_url="https://api.misar.io/mail/v1",  # default
    timeout=30,                       # seconds
    max_retries=3,
)

Async client

from misarmail import AsyncMisarMail

client = AsyncMisarMail(api_key="msk_YOUR_KEY")

All methods on AsyncMisarMail are coroutines — use await.

Email

Send email

result = client.email.send(
    from_={"email": "[email protected]", "name": "Your Name"},
    to=[{"email": "[email protected]"}],
    subject="Hello!",
    html="<p>Hello from MisarMail</p>",
    text="Hello from MisarMail",
)
print(result.message_id)

Send with template

client.email.send(
    from_={"email": "[email protected]"},
    to=[{"email": "[email protected]"}],
    template_id="tmpl_01ABCDEF",
    variables={"first_name": "Alice", "order_id": "1234"},
)

Async send

import asyncio

async def main():
    result = await client.email.send(
        from_={"email": "[email protected]"},
        to=[{"email": "[email protected]"}],
        subject="Hello!",
        html="<p>Hi</p>",
    )
    print(result.message_id)

asyncio.run(main())

Campaigns

# List
page = client.campaigns.list(status="draft", limit=20)
for campaign in page.data:
    print(campaign.name, campaign.status)

# Create
campaign = client.campaigns.create(
    name="June Newsletter",
    subject="What's new",
    from_email="[email protected]",
    html="<p>...</p>",
)

# Send immediately
client.campaigns.send(campaign.id)

# Schedule
from datetime import datetime, timezone
client.campaigns.send(
    campaign.id,
    scheduled_at=datetime(2025, 6, 15, 9, 0, tzinfo=timezone.utc),
)

Contacts

# List
page = client.contacts.list(status="active", limit=50)

# Create
contact = client.contacts.create(
    email="[email protected]",
    first_name="Alice",
    tags=["customer"],
    metadata={"plan": "pro"},
)

# Update
client.contacts.update(contact.id, tags=["customer", "vip"])

# Bulk import
result = client.contacts.import_contacts(
    contacts=[
        {"email": "[email protected]"},
        {"email": "[email protected]"},
    ],
    tags=["imported"],
)
print(f"Imported: {result.imported}, failed: {result.failed}")

Templates

# Create
template = client.templates.create(
    name="Welcome",
    type="transactional",
    subject="Welcome, {{first_name}}!",
    html="<h1>Hi {{first_name}}!</h1>",
    variables=["first_name"],
)

# Preview
preview = client.templates.preview(
    template.id,
    variables={"first_name": "Alice"},
)
print(preview.html)

# Render inline
rendered = client.templates.render(
    html="<p>Hello {{name}}!</p>",
    subject="Hello {{name}}",
    variables={"name": "Alice"},
)

Analytics

metrics = client.analytics.get(
    start_date="2025-06-01",
    end_date="2025-06-30",
    granularity="day",
)
print(f"Open rate: {metrics.open_rate:.1%}")

insights = client.analytics.insights()
print(insights.summary)

Validation

# Single
check = client.validate.check("[email protected]")
print(check.valid, check.result)  # True, "deliverable"

# Bulk
results = client.validate.bulk(["[email protected]", "bad@email"])
for r in results.results:
    print(r.email, r.result)

Tracking

client.tracking.event(
    email="[email protected]",
    event="trial_started",
    properties={"plan": "pro"},
)

client.tracking.purchase(
    email="[email protected]",
    order_id="order_1234",
    revenue=49.99,
    currency="USD",
)

AI

subjects = client.ai.subject_lines(
    context="B2B SaaS product newsletter",
    count=5,
    tone="professional",
)
for s in subjects.subjects:
    print(s)

Error handling

from misarmail import MisarMailError

try:
    client.email.send(
        from_={"email": "[email protected]"},
        to=[{"email": "[email protected]"}],
        subject="Test",
        html="<p>Test</p>",
    )
except MisarMailError as e:
    print(e.status_code)   # HTTP status (int)
    print(e.error)         # machine-readable key (str)
    print(e.message)       # human-readable message (str)
    print(e.request_id)    # for support tickets

Types

All response objects are dataclasses with typed fields:

from misarmail import (
    EmailAddress,
    SendEmailOptions,
    SendEmailResponse,
    Contact,
    ContactStatus,
    Campaign,
    CampaignStatus,
    EmailTemplate,
    TemplateType,
    ImportContactsResponse,
)

Using environment variables

The SDK reads MISARMAIL_API_KEY automatically if api_key is not passed:

export MISARMAIL_API_KEY=msk_YOUR_KEY
from misarmail import MisarMail

client = MisarMail()  # picks up MISARMAIL_API_KEY