Postmark

raw JSON →
1.0 verified Tue May 12 auth: yes python install: verified quickstart: stale maintenance

Python client for the Postmark transactional email API. No official Python SDK exists — postmarker is the primary community library (listed by Postmark on their developer docs). Maintained by Dmitry Dygalo. v1.0 released but project marked inactive on Snyk (no new PyPI releases in 12+ months as of mid-2025). Django email backend included.

pip install postmarker
error AttributeError: module 'postmarker' has no attribute 'PostmarkClient'
cause The `PostmarkClient` class is located within the `postmarker.core` submodule, not directly under the top-level `postmarker` package.
fix
Use from postmarker.core import PostmarkClient to correctly import the client class.
error ImproperlyConfigured: You should specify TOKEN to use Postmark email backend
cause When using `postmarker.django.EmailBackend`, the `TOKEN` key is missing from the `POSTMARK` dictionary in your Django project's `settings.py`.
fix
Add the POSTMARK setting with your server API token to your settings.py file: POSTMARK = {'TOKEN': 'YOUR_POSTMARK_SERVER_TOKEN'}.
error PostmarkerException: Sender Signature not defined for From address.
cause The 'From' email address specified in your email message has not been added and verified as a Sender Signature or a verified domain in your Postmark account.
fix
Log into your Postmark account, navigate to 'Sender Signatures', and add and verify the email address you intend to use in the 'From' field.
error PostmarkerException: Connection Timed Out
cause This error typically occurs when your application cannot establish a connection to Postmark's SMTP servers, often due to outbound traffic on port 25 being blocked by your ISP or hosting provider.
fix
Switch your SMTP port to 2525 or 587 in your application's configuration, as Postmark supports these alternative ports to bypass common blocks on port 25.
error PostmarkerException: Authentication Failed
cause The Postmark API token provided in your `PostmarkClient` initialization or Django settings is incorrect, contains typos, or is an Account API Token instead of a Server API Token.
fix
Verify that you are using the correct Server API Token for the specific Postmark server and ensure there are no leading or trailing spaces or other typos.
gotcha postmarker is a community library, not an official Postmark SDK. Postmark has no official Python SDK. postmarker is the recommended community option per Postmark's developer docs, but it is not maintained by Postmark.
fix For actively maintained alternatives, use django-anymail[postmark] for Django projects, or make direct HTTP calls using requests to the Postmark REST API.
gotcha postmarker v1.0 has no releases to PyPI in 12+ months as of mid-2025. The project may be effectively abandoned. Python 3.11+ compatibility has not been verified in recent releases.
fix Test compatibility with your Python version before relying on postmarker in production. Consider django-anymail as a more actively maintained alternative.
gotcha Field names in postmarker follow Postmark's PascalCase API convention, not Python snake_case. Code passing lowercase fields (from_email, html_body) is silently ignored or raises AttributeError.
fix Use From=, To=, HtmlBody=, TextBody=, Subject= (PascalCase) as per Postmark API docs.
gotcha PostmarkClient constructor kwarg is server_token=, not token=. An earlier version used token= — code from older tutorials passes token= and raises TypeError.
fix PostmarkClient(server_token='YOUR_TOKEN')
gotcha Use 'POSTMARK_API_TEST' as the server_token to send emails to Postmark's blackhole test environment (test@blackhole.postmarkapp.com). These emails are never delivered. Switch to a real server token for production.
fix PostmarkClient(server_token='POSTMARK_API_TEST') for testing, PostmarkClient(server_token=os.environ['POSTMARK_SERVER_TOKEN']) for production.
pip install django-anymail[postmark]
python os / libc variant status wheel install import disk
3.10 alpine (musl) postmark - - - -
3.10 alpine (musl) postmarker - - 0.67s 21.4M
3.10 slim (glibc) postmark - - - -
3.10 slim (glibc) postmarker - - 0.47s 22M
3.11 alpine (musl) postmark - - - -
3.11 alpine (musl) postmarker - - 0.89s 23.5M
3.11 slim (glibc) postmark - - - -
3.11 slim (glibc) postmarker - - 0.70s 24M
3.12 alpine (musl) postmark - - - -
3.12 alpine (musl) postmarker - - 0.77s 15.3M
3.12 slim (glibc) postmark - - - -
3.12 slim (glibc) postmarker - - 0.80s 16M
3.13 alpine (musl) postmark - - - -
3.13 alpine (musl) postmarker - - 0.75s 14.9M
3.13 slim (glibc) postmark - - - -
3.13 slim (glibc) postmarker - - 0.73s 15M
3.9 alpine (musl) postmark - - - -
3.9 alpine (musl) postmarker - - 0.63s 20.7M
3.9 slim (glibc) postmark - - - -
3.9 slim (glibc) postmarker - - 0.51s 21M

Field names match Postmark API exactly (PascalCase, not snake_case). Use 'POSTMARK_API_TEST' as token to send to the test/blackhole sandbox.

import os
from postmarker.core import PostmarkClient

postmark = PostmarkClient(server_token=os.environ['POSTMARK_SERVER_TOKEN'])

# Send single email
response = postmark.emails.send(
    From='sender@example.com',
    To='recipient@example.com',
    Subject='Hello from Postmark',
    HtmlBody='<strong>Hello!</strong>'
)
print(response['MessageID'])

# Send batch
postmark.emails.send_batch(
    {
        'From': 'sender@example.com',
        'To': 'recipient1@example.com',
        'Subject': 'Batch 1',
        'TextBody': 'Hello 1'
    },
    {
        'From': 'sender@example.com',
        'To': 'recipient2@example.com',
        'Subject': 'Batch 2',
        'TextBody': 'Hello 2'
    }
)

# Test token for sandbox
# postmark = PostmarkClient(server_token='POSTMARK_API_TEST')