Skip to content

Python SDK

Terminal window
pip install vaartix

The current version is 0.2.0. Works with Python 3.10 to 3.14, with no dependencies and full type hints. The package is on PyPI and the source is on GitHub at vaartix-mail/vaartix-python. Step-by-step guide: Send email from Django.

Using an AI assistant such as Claude or Cursor? The MCP server lets it send email and look up delivery status through the same API, without writing code.

from vaartix import Vaartix
vaartix = Vaartix() # reads the VAARTIX_API_KEY environment variable
email = vaartix.emails.send(
{
"from": "Acme <[email protected]>",
"to": ["[email protected]"],
"subject": "Hello from Vaartix",
"html": "<strong>It works!</strong>",
}
)
print(email["id"]) # em_...

Emails are plain dicts because from is a Python keyword. Keys can be snake_case (reply_to, scheduled_at) or the API’s camelCase (replyTo, scheduledAt). Responses are the API’s JSON as dicts, exactly as in the API reference.

# Retry safely: the same key within 24 hours returns the first email instead of sending twice.
vaartix.emails.send(email, idempotency_key=f"welcome-{user.id}")
# Send later (up to 30 days ahead), and cancel if plans change.
scheduled = vaartix.emails.send({**email, "scheduled_at": "2026-11-01T09:00:00+05:30"})
vaartix.emails.cancel(scheduled["id"])
# Up to 100 emails in one request.
vaartix.batch.send([email_1, email_2])
# Look up an email and its delivery events, or list recent bounces.
vaartix.emails.get("em_...")
vaartix.emails.list(status="bounced", days=7)

No domain yet? Send from any address at test.vaartix.com to delivered@, bounced@ or [email protected] to simulate what happens:

vaartix.emails.send(
{
"from": "[email protected]",
"subject": "Hello",
"text": "It works!",
}
)

Since vaartix 0.2.0, you can send all of Django’s email through Vaartix, including send_mail, EmailMessage and built-in emails such as password resets:

Terminal window
pip install "vaartix[django]"
settings.py
VAARTIX_API_KEY = os.environ["VAARTIX_API_KEY"]
DEFAULT_FROM_EMAIL = "Acme <[email protected]>" # on a domain verified in Vaartix
# Django 6.1 and newer
MAILERS = {"default": {"BACKEND": "vaartix.django.EmailBackend"}}
# Django 4.2 to 6.0
EMAIL_BACKEND = "vaartix.django.EmailBackend"

With MAILERS you can also pass the key as an option instead: {"BACKEND": "vaartix.django.EmailBackend", "OPTIONS": {"api_key": "vx_live_..."}}.

from django.core.mail import EmailMultiAlternatives
message = EmailMultiAlternatives("Order confirmed", "Thanks!", to=["[email protected]"])
message.attach_alternative("<p>Thanks!</p>", "text/html")
message.tags = [{"name": "type", "value": "order"}] # optional
message.send()
print(message.vaartix_id) # em_...

HTML alternatives, attachments, inline images (Content-ID), cc, bcc, reply-to and custom headers are all supported. Errors raise VaartixError unless you pass fail_silently=True.

API and network errors raise VaartixError:

from vaartix import VaartixError
try:
vaartix.emails.send(email)
except VaartixError as err:
print(err.type, err.status_code, err.message) # e.g. rate_limited 429 Too many requests...

err.type is a stable code; the full list is in Errors. Network failures have type == "network_error" and status_code is None.

Check that a webhook request really came from Vaartix before trusting it. verify_webhook() checks the signature and timestamp (Standard Webhooks) and returns the parsed event. Pass the raw body, not parsed JSON.

import os
from flask import Flask, request
from vaartix import verify_webhook, WebhookVerificationError
app = Flask(__name__)
@app.post("/webhooks/vaartix")
def vaartix_webhook():
try:
event = verify_webhook(
request.get_data(), # raw bytes, not request.json
request.headers,
os.environ["VAARTIX_WEBHOOK_SECRET"],
)
except WebhookVerificationError:
return "", 400
if event["type"] == "email.bounced":
... # stop emailing event["data"]["recipient"]
return "", 200

In Django, use request.body and request.headers:

import os
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from vaartix import verify_webhook, WebhookVerificationError
@csrf_exempt # Vaartix signs the request instead
@require_POST
def vaartix_webhook(request):
try:
event = verify_webhook(
request.body, # raw bytes
request.headers,
os.environ["VAARTIX_WEBHOOK_SECRET"],
)
except WebhookVerificationError:
return HttpResponse(status=400)
if event["type"] == "email.bounced":
... # stop emailing event["data"]["recipient"]
return HttpResponse(status=200)

Manage endpoints with vaartix.webhooks.create(url=..., events=[...]), list(), get(), update(), remove(), rotate_secret(), deliveries() and retry_delivery(). See Webhooks for the events and how retries work.