Skip to main content

mailblastr

Official Python SDK for the MailBlastr email API — send transactional and marketing email from your own verified domain.

Zero dependencies (Python standard library only). Python 3.8+.

Install

pip install mailblastr

Setup

Grab your API key from the MailBlastr dashboard.

import mailblastr

mailblastr.api_key = "mb_xxxxxxxxx"

Send your first email

import mailblastr

mailblastr.api_key = "mb_xxxxxxxxx"

params: mailblastr.Emails.SendParams = {
    "from": "Acme <hello@yourdomain.com>",
    "to": ["user@example.com"],
    "subject": "Hello from MailBlastr",
    "html": "<p>Your first email 🎉</p>",
}

email = mailblastr.Emails.send(params)
print(email["id"])

Every method returns the parsed JSON response. On any non-2xx status the SDK raises mailblastr.MailblastrError carrying the API error body:

try:
    mailblastr.Emails.send(params)
except mailblastr.MailblastrError as e:
    print(e.status_code, e.name, e.message)   # e.g. 422 validation_error "..."

Attachments

Attach files by hosted URL (path, fetched at send time) or inline base64 (content):

mailblastr.Emails.send({
    "from": "Acme <hello@yourdomain.com>",
    "to": ["user@example.com"],
    "subject": "Your invoice",
    "html": "<p>Invoice attached.</p>",
    "attachments": [
        {"filename": "invoice.pdf", "path": "https://yourdomain.com/invoices/invoice.pdf"},
        {"filename": "report.csv", "content": base64_content, "content_type": "text/csv"},
    ],
})

Batch send

mailblastr.Batch.send([
    {"from": "hello@yourdomain.com", "to": ["a@example.com"], "subject": "Hi A", "html": "<p>A</p>"},
    {"from": "hello@yourdomain.com", "to": ["b@example.com"], "subject": "Hi B", "html": "<p>B</p>"},
])  # up to 100 emails per request

Options

mailblastr.base_url = "https://www.mailblastr.com/api"   # override your API host
mailblastr.timeout = 30            # per-request timeout in seconds (default 30)
mailblastr.max_retries = 2         # auto-retry 429/503 responses (default 2; 0 disables)

Requests time out after 30 seconds by default. A 429 (rate limited) or 503 (service unavailable) response is retried up to max_retries times, honoring the Retry-After header (otherwise exponential backoff). Only those two statuses are retried — never other errors, network failures, or timeouts — so a non-idempotent request (like sending an email) is never duplicated by a retry.

The domain-first model

MailBlastr is DOMAIN-FIRST: each of your verified sending domains has its own contact pool — the same address on two domains is two records with separate consent. That means:

  • Contacts take a domain (required to create/list on the flat /contacts API; disambiguates an email id on get/update/remove).
  • Segments and Topics belong to a domain (domain required on create and list).
  • Campaigns.create REQUIRES domain — it picks the contact pool the campaign targets (the from address may be a different verified domain).
  • Automations.create REQUIRES domain, and Events.send REQUIRES domain — only automations belonging to that domain are triggered, so the same event name (e.g. user.created) across several products can never double-fire.

Resources

Each resource is a class with methods following a consistent create / get / list / update / remove shape (plus resource-specific verbs): Emails (with nested Emails.Attachments and Emails.Receiving), Batch, Domains, Audiences, Contacts, ContactProperties, Campaigns, Segments, Topics, Templates, Automations, Webhooks, Events, ApiKeys, Logs, Polls.

# Emails
mailblastr.Emails.send(params)
mailblastr.Emails.list({"limit": 20, "after": cursor})   # cursor pagination
mailblastr.Emails.get(email_id)
mailblastr.Emails.update(email_id, {"scheduled_at": "2026-08-01T09:00:00Z"})  # reschedule
mailblastr.Emails.cancel(email_id)
mailblastr.Emails.Attachments.list(email_id)
mailblastr.Emails.Attachments.get(email_id, attachment_id)

# Inbound email
mailblastr.Emails.Receiving.list()
mailblastr.Emails.Receiving.get(email_id)
mailblastr.Emails.Receiving.attachments(email_id)
mailblastr.Emails.Receiving.get_attachment(email_id, attachment_id)  # -> bytes
mailblastr.Emails.Receiving.raw(email_id)                            # -> bytes (RFC822)
mailblastr.Emails.Receiving.forward(email_id, {"from": "me@yourdomain.com", "to": "team@you.com"})
mailblastr.Emails.Receiving.reply(email_id, {"from": "me@yourdomain.com", "html": "<p>Thanks!</p>"})
mailblastr.Emails.Receiving.remove(email_id)

# Domains (incl. claiming a domain verified elsewhere + one-click DNS)
mailblastr.Domains.create({"name": "yourdomain.com"})
mailblastr.Domains.verify(domain_id)
mailblastr.Domains.claim({"name": "yourdomain.com"})
mailblastr.Domains.verify_claim(domain_id)
mailblastr.Domains.detect_dns(domain_id)
mailblastr.Domains.apply_cloudflare_dns(domain_id, {"token": cf_token})

# Contacts (domain-first)
mailblastr.Contacts.create({"domain": "yourdomain.com", "email": "user@example.com", "first_name": "Ada"})
mailblastr.Contacts.list({"domain": "yourdomain.com"})
mailblastr.Contacts.get({"id": contact_id})                                # by id (exact) …
mailblastr.Contacts.get({"id": "user@example.com", "domain": "yourdomain.com"})  # … or by email + domain
mailblastr.Contacts.update({"id": contact_id, "unsubscribed": True})
mailblastr.Contacts.remove({"id": contact_id})
mailblastr.Contacts.batch({"audience_id": aud_id, "contacts": [{"email": "a@b.com"}]})
mailblastr.Contacts.import_csv({"audience_id": aud_id, "csv": "email,company\na@b.com,Acme"})
mailblastr.Contacts.add_to_segment(contact_id, segment_id)
mailblastr.Contacts.list_segments(contact_id)
mailblastr.Contacts.update_topics(contact_id, {"topics": [{"id": topic_id, "subscription": "opt_in"}]})

# Contact properties (custom fields / merge tags)
mailblastr.ContactProperties.create({"key": "plan", "type": "string"})

# Campaigns & Segments (domain-first)
mailblastr.Campaigns.create({"domain": "yourdomain.com", "from": sender, "subject": subject, "html": html})
mailblastr.Campaigns.send(campaign_id, {"scheduled_at": "tomorrow 9am"})
mailblastr.Campaigns.stats(campaign_id)
mailblastr.Campaigns.ab(campaign_id)
mailblastr.Segments.create({"domain": "yourdomain.com", "name": "VIP", "filter": {"status": "subscribed"}})
mailblastr.Segments.list({"domain": "yourdomain.com"})
mailblastr.Segments.contacts(segment_id)   # preview who matches

# Topics (domain-first)
mailblastr.Topics.create({"domain": "yourdomain.com", "name": "Product updates", "default_subscription": "opt_in"})
mailblastr.Topics.list({"domain": "yourdomain.com"})

# Templates
mailblastr.Templates.create({"name": "Welcome", "subject": "Hi {{first_name}}", "html": html})
mailblastr.Templates.duplicate(template_id)
mailblastr.Templates.publish(template_id)
mailblastr.Emails.send({"from": sender, "to": to, "template_id": tmpl_id, "variables": {"first_name": "Ada"}})

# Audiences
mailblastr.Audiences.list()
mailblastr.Audiences.import_sheet(audience_id, {"url": sheet_url})

# API keys
mailblastr.ApiKeys.create({"name": "CI", "permission": "sending_access"})
mailblastr.ApiKeys.list()
mailblastr.ApiKeys.remove(key_id)

# Logs & Polls
mailblastr.Logs.list({"limit": 100, "method": "POST", "status": 429})
mailblastr.Logs.get(log_id)
mailblastr.Polls.list()
mailblastr.Polls.get(email_id)

Automations & Events

Every automation belongs to one of your sending domains — domain is required on create, and Events.send names the domain it targets.

automation = mailblastr.Automations.create({
    "name": "Welcome series",
    "domain": "yourdomain.com",
    "trigger": "contact.created",
})

mailblastr.Automations.add_step(automation["id"], {
    "type": "send_email",
    "config": {"template_id": "tmpl_welcome"},
})
mailblastr.Automations.update(automation["id"], {"status": "enabled"})

# Fire a custom event — only yourdomain.com's automations are triggered
mailblastr.Events.send({
    "event": "signup.completed",
    "domain": "yourdomain.com",
    "email": "user@example.com",
    "payload": {"plan": "pro"},
})

# Inspect execution
runs = mailblastr.Automations.runs(automation["id"], {"limit": 25})
mailblastr.Automations.get_run(automation["id"], runs["data"][0]["id"])
mailblastr.Automations.stop(automation["id"])

Webhooks

hook = mailblastr.Webhooks.create({
    "endpoint": "https://yourapp.com/hooks/mailblastr",
    "events": ["email.delivered", "email.bounced", "contact.unsubscribed"],
})
signing_secret = hook["signing_secret"]   # shown ONCE, only here

mailblastr.Webhooks.list()
mailblastr.Webhooks.update(hook["id"], {"status": "disabled"})
mailblastr.Webhooks.rotate(hook["id"])    # new secret, returned once
mailblastr.Webhooks.test(hook["id"])

Verify incoming deliveries locally (no HTTP request) — pass the EXACT raw request body string, the svix-* headers, and your signing secret:

result = mailblastr.Webhooks.verify(raw_body, request.headers, signing_secret)
if not result["valid"]:
    abort(401)   # result["reason"] says why, e.g. 'no_match'

Pagination

list() methods accept optional cursor pagination — {"limit", "after", "before"}:

mailblastr.Campaigns.list({"limit": 25, "after": "cursor_abc"})

Idempotency

Pass an idempotency key to safely retry a create (24h window):

mailblastr.Emails.send(params, options={"idempotency_key": "order-123"})

Documentation

Full docs: https://www.mailblastr.com/docs

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mailblastr-1.1.0.tar.gz (28.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mailblastr-1.1.0-py3-none-any.whl (27.1 kB view details)

Uploaded Python 3

File details

Details for the file mailblastr-1.1.0.tar.gz.

File metadata

  • Download URL: mailblastr-1.1.0.tar.gz
  • Upload date:
  • Size: 28.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for mailblastr-1.1.0.tar.gz
Algorithm Hash digest
SHA256 82dd4557d31951bef8743dafc5043e324918e7f29bb9cdf670e16eb641da5885
MD5 7d77ab8b81e487744549581f6d3749e4
BLAKE2b-256 7e09b3e86fb0481bb7d7bc82d417d1602154d0408fd277b4d2ed627876cd46cb

See more details on using hashes here.

Provenance

The following attestation bundles were made for mailblastr-1.1.0.tar.gz:

Publisher: release.yml on shekhu10/mailblastr-sdks

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mailblastr-1.1.0-py3-none-any.whl.

File metadata

  • Download URL: mailblastr-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 27.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for mailblastr-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 01b474e862fc6e24a3bfacef5beba4103c496236cda1a8ab06fdd622a6dec1c5
MD5 a9cb13e8196b5778a7e67b7670cd1ee8
BLAKE2b-256 9936d4b400d4af16ab0af42334d84cdda544c3a290c21a3cc2455c80826a15d4

See more details on using hashes here.

Provenance

The following attestation bundles were made for mailblastr-1.1.0-py3-none-any.whl:

Publisher: release.yml on shekhu10/mailblastr-sdks

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

5.1.1

2 files

5.1.0

2 files

5.0.1

2 files

5.0.0

2 files

4.0.0

2 files

3.0.1

2 files

3.0.0

2 files

2.0.0

2 files

1.3.0

2 files

1.2.0

2 files

This release

1.1.0 This release

2 files

1.0.0

2 files

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