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.
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